diff --git a/.github/actions/ci_relay/action.yml b/.github/actions/ci_relay/action.yml index 0346f9a53..f6986acc0 100644 --- a/.github/actions/ci_relay/action.yml +++ b/.github/actions/ci_relay/action.yml @@ -33,6 +33,5 @@ runs: run: echo $SECRETS_PROPERTIES | base64 --decode > secrets.properties - name: Relay integration tests - # TODO: Add SDK_VERSION env var shell: bash - run: ./gradlew :foundation:test --tests "com.walletconnect.foundation.RelayTest" -i \ No newline at end of file + run: ./gradlew :foundation:test --tests "com.reown.foundation.RelayTest" -i \ No newline at end of file diff --git a/.github/workflows/ci_db_migrations.yml b/.github/workflows/ci_db_migrations.yml index 9162ebea6..23d52d1cc 100644 --- a/.github/workflows/ci_db_migrations.yml +++ b/.github/workflows/ci_db_migrations.yml @@ -23,7 +23,6 @@ jobs: conf: [ { name: Android-Core SDK, command: ":core:android:verifyDebugAndroidCoreDatabaseMigration" }, { name: Sign SDK, command: ":protocol:sign:verifyDebugSignDatabaseMigration" }, - { name: Auth SDK, command: ":protocol:auth:verifyDebugAuthDatabaseMigration" }, { name: Notify SDK, command: ":protocol:notify:verifyDebugNotifyDatabaseMigration" }, ] name: ${{ matrix.conf.name }} diff --git a/.github/workflows/ci_sonarcloud.yml b/.github/workflows/ci_sonarcloud.yml index e19745af7..df4a080fb 100644 --- a/.github/workflows/ci_sonarcloud.yml +++ b/.github/workflows/ci_sonarcloud.yml @@ -1,12 +1,15 @@ name: SonarCloud on: - push: - branches: - - develop - - master - pull_request: - types: [opened, synchronize, reopened] + workflow_dispatch: +#TODO: user sonar when repos are public +#on: +# push: +# branches: +# - develop +# - master +# pull_request: +# types: [opened, synchronize, reopened] concurrency: # Support push/pr as event types with different behaviors each: diff --git a/.github/workflows/ci_verify_snapshots.yml b/.github/workflows/ci_verify_snapshots.yml deleted file mode 100644 index 02a7c0712..000000000 --- a/.github/workflows/ci_verify_snapshots.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Verify Snapshots - -on: - workflow_dispatch: - -jobs: - verify_snapshots: - strategy: - matrix: - conf: [ - { name: wcm, command: ":product:walletconnectmodal:verifyPaparazziDebug"} - ] - name: ${{ matrix.conf.name }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Java 17 - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '17' - architecture: x86_64 - cache: 'gradle' - - - name: Cache Gradle - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Fetch Properties File - env: - SECRETS_PROPERTIES: ${{ secrets.SECRETS_PROPERTIES }} - run: echo $SECRETS_PROPERTIES | base64 --decode > secrets.properties - - - name: Verify snapshots - run: ./gradlew ${{ matrix.conf.command }} - - - name: Stop Gradle - run: ./gradlew --stop diff --git a/ReadMe.md b/ReadMe.md index 48be1d964..1732c5a2d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,80 +1,31 @@ -![WalletConnect V2](/docs/walletconnect-banner.svg) +# **Reown - Kotlin** -# **WalletConnect - Kotlin** - -The communications protocol for web3, WalletConnect brings the ecosystem together by enabling hundreds of wallets and apps to securely connect and interact. This repository contains Kotlin implementation of -WalletConnect v2 protocols for Android applications. +The communications protocol for web3, Reown brings the ecosystem together by enabling hundreds of wallets and apps to securely connect and interact. This repository contains Kotlin implementation of +Reown protocols for Android applications. #### ## BOM Instructions: -To help manage compatible dependencies stay in sync, we've introduced a [BOM](https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import) to the Kotlin SDK. With this, you only need to update the BOM version to get the latest SDKs. Just add the BOM as a dependency and then list the SDKs you want to include into your project. + +To help manage compatible dependencies stay in sync, we've introduced a [BOM](https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import) to the Kotlin SDK. With this, you only need to +update the BOM version to get the latest SDKs. Just add the BOM as a dependency and then list the SDKs you want to include into your project. ### example build.gradle.kts + ```kotlin dependencies { - implementation(platform("com.walletconnect:android-bom:{BOM version}")) - implementation("com.walletconnect:android-core") - implementation("com.walletconnect:web3wallet") + implementation(platform("com.reown:android-bom:{BOM version}")) + implementation("com.reown:android-core") + implementation("com.reown:walletkit") } ``` ## SDK Chart -| BOM | [Core SDK](core/android) | [Sign SDK](protocol/sign) | [Auth SDK](protocol/auth) | [Chat SDK](protocol/chat) | [Notify SDK](protocol/notify) | [web3wallet](product/web3wallet) | [web3modal](product/web3modal) | [WalletConnectModal](product/walletconnectmodal) | -|-----------------------------------------------------------------------------------------|--------------------------|---------------------------|---------------------------|---------------------------|:------------------------------|----------------------------------|--------------------------------|--------------------------------------------------| -| 1.35.1 | 1.35.1 | 2.35.1 | 1.28.9 | 1.0.0.beta36 | 1.3.10 | 1.35.1 | 1.6.5 | 1.5.10 | -| 1.35.0 | 1.35.0 | 2.35.0 | 1.28.8 | 1.0.0.beta35 | 1.3.9 | 1.35.0 | 1.6.4 | 1.5.9 | -| 1.34.1 | 1.34.1 | 2.34.1 | 1.28.7 | 1.0.0.beta34 | 1.3.8 | 1.34.1 | 1.6.3 | 1.5.8 | -| 1.34.0 | 1.34.0 | 2.34.0 | 1.28.6 | 1.0.0.beta33 | 1.3.7 | 1.34.0 | 1.6.2 | 1.5.7 | -| 1.33.1 | 1.33.1 | 2.33.1 | 1.28.5 | 1.0.0.beta32 | 1.3.6 | 1.33.1 | 1.6.1 | 1.5.6 | -| 1.33.0 | 1.33.0 | 2.33.0 | 1.28.4 | 1.0.0.beta31 | 1.3.5 | 1.33.0 | 1.6.0 | 1.5.5 | -| 1.32.1 | 1.32.0 | 2.32.0 | 1.28.3 | 1.0.0.beta30 | 1.3.4 | 1.32.1 | 1.5.4 | 1.5.4 | -| 1.31.3 | 1.31.2 | 2.31.2 | 1.28.2 | 1.0.0.beta30 | 1.3.2 | 1.31.2 | 1.5.2 | 1.5.2 | -| 1.31.1 | 1.31.1 | 2.31.1 | 1.28.1 | 1.0.0.beta26 | 1.3.1 | 1.31.1 | 1.5.1 | 1.5.1 | -| 1.31.0[**](https://gist.github.com/TalhaAli00/7b9e1cadf19b9dc5141cd033aa4e6172) | 1.31.0 | 2.31.0 | 1.28.0 | 1.0.0.beta25 | 1.3.0 | 1.31.0 | 1.5.0 | 1.5.0 | -| 1.30.0 | 1.30.0 | 2.30.0 | 1.27.0 | 1.0.0.beta25 | 1.2.0 | 1.30.0 | 1.4.0 | 1.4.0 | -| 1.23.0 | 1.28.0 | 2.26.0 | 1.26.0 | 1.0.0.beta25 | 1.1.0 | 1.21.0 | 1.3.0 | 1.3.0 | -| 1.22.2 | 1.27.2 | 2.25.2 | 1.25.2 | 1.0.0.beta25 | 1.0.2 | 1.20.2 | 1.2.2 | 1.2.2 | -| 1.22.1 | 1.27.0 | 2.25.0 | 1.25.0 | 1.0.0.beta23 | 1.0.0 | 1.20.0 | 1.2.1 | 1.2.0 | -| 1.22.0 | 1.27.0 | 2.25.0 | 1.25.0 | 1.0.0.beta23 | 1.0.0 | 1.20.0 | 1.2.0 | 1.2.0 | -| 1.21.1 | 1.26.0 | 2.24.0 | 1.24.0 | 1.0.0.beta23 | 1.0.0-beta04 | 1.19.0 | 1.1.1 | 1.1.1 | -| 1.21.0 | 1.26.0 | 2.24.0 | 1.24.0 | 1.0.0.beta23 | 1.0.0-beta04 | 1.19.0 | 1.1.0 | 1.1.0 | -| 1.20.0 | 1.25.0 | 2.23.0 | 1.23.0 | 1.0.0.beta22 | 1.0.0-beta03 | 1.18.0 | 1.0.0 | 1.0.0-beta02 | -| 1.19.1 | 1.24.0 | 2.22.0 | 1.22.0 | 1.0.0.beta21 | 1.0.0-beta02 | 1.17.0 | 1.0.0-beta02 | 1.0.0-beta01 | -| 1.19.0 | 1.24.0 | 2.22.0 | 1.22.0 | 1.0.0.beta21 | 1.0.0-beta02 | 1.17.0 | 1.0.0-beta01 | 1.0.0-beta01 | -| 1.18.0 | 1.23.0 | 2.21.0 | 1.21.0 | 1.0.0.beta20 | 1.0.0-beta01 | 1.16.0 | 1.0.0-alpha11 | 1.0.0-alpha07 | -| 1.17.2 | 1.22.1 | 2.20.1 | 1.20.1 | 1.0.0.beta19 | 1.0.0-alpha05 | 1.15.1 | 1.0.0-alpha10 | 1.0.0-alpha06 | -| 1.17.1 | 1.22.0 | 2.20.0 | 1.20.0 | 1.0.0.beta18 | 1.0.0-alpha04 | 1.15.0 | 1.0.0-alpha09 | 1.0.0-alpha05 | -| 1.17.0 | 1.22.0 | 2.20.0 | 1.20.0 | 1.0.0.beta18 | 1.0.0-alpha03 | 1.15.0 | 1.0.0-alpha09 | 1.0.0-alpha05 | -| 1.16.0 | 1.21.0 | 2.19.0 | 1.19.0 | 1.0.0.beta17 | 1.0.0-alpha02 | 1.14.0 | | 1.0.0-alpha04 | -| 1.15.0 | 1.20.0 | 2.18.0 | 1.18.0 | 1.0.0-beta16 | | 1.13.0 | | 1.0.0-alpha03 | -| 1.14.0 | 1.19.0 | 2.17.0 | 1.17.0 | 1.0.0-beta15 | | 1.12.0 | | 1.0.0-alpha02 | -| 1.13.1 | 1.18.0 | 2.16.0 | 1.16.0 | 1.0.0-beta14 | | 1.11.0 | | 1.0.0-alpha01 | -| 1.12.0 | 1.17.0 | 2.15.0 | 1.15.0 | 1.0.0-beta13 | | 1.10.0 | | | -| 1.11.1 | 1.16.1 | 2.14.1 | 1.14.1 | 1.0.0-beta12 | | 1.9.1 | | | -| 1.11.0 | 1.16.0 | 2.14.0 | 1.14.0 | 1.0.0-beta11 | | 1.9.0 | | | -| 1.10.0 | 1.15.0 | 2.13.0 | 1.13.0 | 1.0.0-beta10 | | 1.8.0 | | | -| 1.9.3 | 1.14.3 | 2.12.3 | 1.12.3 | 1.0.0-beta09 | | 1.7.3 | | | -| 1.9.2 | 1.14.2 | 2.12.2 | 1.12.2 | 1.0.0-beta08 | | 1.7.2 | | | -| 1.9.1 | 1.14.1 | 2.12.1 | 1.12.1 | 1.0.0-beta07 | | 1.7.1 | | | -| 1.9.0[**](https://github.com/WalletConnect/WalletConnectKotlinV2/issues/821) | 1.14.0 | 2.12.0 | 1.12.0 | 1.0.0-beta06 | | 1.7.0 | | | -| 1.8.0 | 1.13.0 | 2.11.0 | 1.11.0 | 1.0.0-beta05 | | 1.6.0 | | | -| 1.7.0 | 1.12.0 | 2.10.0 | 1.10.0 | 1.0.0-beta04 | | 1.5.0 | | | -| 1.6.1 | 1.11.1 | 2.9.1 | 1.9.1 | 1.0.0-beta03 | | 1.4.1 | | | -| 1.6.0 | 1.11.0 | 2.9.0 | 1.9.0 | 1.0.0-beta02 | | 1.4.0 | | | -| 1.5.0 | 1.10.0 | 2.8.0 | 1.8.0 | 1.0.0-beta01 | | 1.3.0 | | | -| 1.4.1 | 1.9.1 | 2.7.1 | 1.7.1 | 1.0.0-alpha09 | | 1.2.1 | | | -| 1.3.0 | 1.8.0 | 2.6.0 | 1.6.0 | 1.0.0-alpha07 | | 1.1.0 | | | -| 1.2.0 | 1.7.0 | 2.5.0 | 1.5.0 | 1.0.0-alpha06 | | 1.0.0 | | | -| 1.1.1 | 1.6.0 | 2.4.0 | 1.4.0 | 1.0.0-alpha05 | | | | | -| 1.0.1 | 1.5.0 | 2.3.1 | 1.3.0 | 1.0.0-alpha04 | | | | | -| | 1.4.0 | 2.2.0 | 1.2.0 | 1.0.0-alpha03 | | | | | -| | 1.3.0 | 2.1.0 | 1.1.0 | 1.0.0-alpha02 | | | | | -| | 1.2.0 | | | 1.0.0-alpha01 | | | | | -| | 1.1.0 | 2.0.0 | 1.0.0 | | | | | | -| | 1.0.0 | 2.0.0-rc.5 | 1.0.0-alpha01 | | | | | | - +| BOM | [Core SDK](core/android) | [Sign SDK](protocol/sign) | [WalletKit](product/walletkit) | [AppKit](product/appkit) | +|-------|--------------------------|---------------------------|--------------------------------|--------------------------| +| 1.0.0 | 1.0.0 | 1.0.0 | 1.0.0 | 1.0.0 | ## License -WalletConnect v2 is released under the Apache 2.0 license. [See LICENSE](/LICENSE) for details. + +Reown is released under the Apache 2.0 license. [See LICENSE](/LICENSE) for details. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index ae9ae4dee..9ebcc0592 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,38 +35,40 @@ allprojects { } } -sonar { - properties { - properties( - mapOf( - "sonar.projectKey" to "WalletConnect_WalletConnectKotlinV2", - "sonar.organization" to "walletconnect", - "sonar.host.url" to "https://sonarcloud.io", - "sonar.gradle.skipCompile" to true, - "sonar.coverage.exclusions" to "sample/**,**/di/**,/buildSrc/**,**/gradle/**,**/test/**,**/androidTest/**,**/build.gradle.kts", - ) - ) - } -} +////todo: user sonar cloud after repos are public +//sonar { +// properties { +// properties( +// mapOf( +// "sonar.projectKey" to "WalletConnect_WalletConnectKotlinV2", +// "sonar.organization" to "walletconnect", +// "sonar.host.url" to "https://sonarcloud.io", +// "sonar.gradle.skipCompile" to true, +// "sonar.coverage.exclusions" to "sample/**,**/di/**,/buildSrc/**,**/gradle/**,**/test/**,**/androidTest/**,**/build.gradle.kts", +// ) +// ) +// } +//} subprojects { apply(plugin = rootProject.libs.plugins.sonarqube.get().pluginId) - extensions.configure { - setAndroidVariant("debug") - - isSkipProject = name == "bom" - properties { - properties( - mapOf( - "sonar.gradle.skipCompile" to true, - "sonar.sources" to "${projectDir}/src/main/kotlin", - "sonar.java.binaries" to layout.buildDirectory, - "sonar.coverage.jacoco.xmlReportPaths" to "${layout.buildDirectory}/reports/jacoco/xml/jacoco.xml" - ) - ) - } - } +////todo: user sonar cloud after repos are public +// extensions.configure { +// setAndroidVariant("debug") +// +// isSkipProject = name == "bom" +// properties { +// properties( +// mapOf( +// "sonar.gradle.skipCompile" to true, +// "sonar.sources" to "${projectDir}/src/main/kotlin", +// "sonar.java.binaries" to layout.buildDirectory, +// "sonar.coverage.jacoco.xmlReportPaths" to "${layout.buildDirectory}/reports/jacoco/xml/jacoco.xml" +// ) +// ) +// } +// } afterEvaluate { if (hasProperty("android")) { @@ -332,11 +334,8 @@ private val repoIdWithVersion = listOf( Pair(FOUNDATION, FOUNDATION_VERSION), Pair(ANDROID_CORE, CORE_VERSION), Pair(SIGN, SIGN_VERSION), - Pair(AUTH, AUTH_VERSION), - Pair(CHAT, CHAT_VERSION), Pair(NOTIFY, NOTIFY_VERSION), - Pair(WEB_3_WALLET, WEB_3_WALLET_VERSION), - Pair(WEB_3_MODAL, WEB_3_MODAL_VERSION), - Pair(WC_MODAL, WC_MODAL_VERSION), + Pair(WALLETKIT, WALLETKIT_VERSION), + Pair(APPKIT, APPKIT_VERSION), Pair(MODAL_CORE, MODAL_CORE_VERSION) ) \ No newline at end of file diff --git a/buildSrc/scripts/check_modules.sh b/buildSrc/scripts/check_modules.sh index f8597b1df..e2fd17a2f 100755 --- a/buildSrc/scripts/check_modules.sh +++ b/buildSrc/scripts/check_modules.sh @@ -33,8 +33,6 @@ check_changes "foundation" "FOUNDATION_MODULE_CHANGED" check_changes "core/android" "CORE_MODULE_CHANGED" check_changes "core/modal" "MODAL_CORE_MODULE_CHANGED" check_changes "protocol/sign" "SIGN_MODULE_CHANGED" -check_changes "protocol/auth" "AUTH_MODULE_CHANGED" -check_changes "protocol/chat" "CHAT_MODULE_CHANGED" check_changes "protocol/notify" "NOTIFY_MODULE_CHANGED" check_changes "product/web3wallet" "WEB_3_WALLET_MODULE_CHANGED" check_changes "product/walletconnectmodal" "WC_MODAL_MODULE_CHANGED" @@ -46,8 +44,6 @@ echo "FOUNDATION_MODULE_CHANGED=$FOUNDATION_MODULE_CHANGED" >> "$1" echo "CORE_MODULE_CHANGED=$CORE_MODULE_CHANGED" >> "$1" echo "MODAL_CORE_MODULE_CHANGED=$MODAL_CORE_MODULE_CHANGED" >> "$1" echo "SIGN_MODULE_CHANGED=$SIGN_MODULE_CHANGED" >> "$1" -echo "AUTH_MODULE_CHANGED=$AUTH_MODULE_CHANGED" >> "$1" -echo "CHAT_MODULE_CHANGED=$CHAT_MODULE_CHANGED" >> "$1" echo "NOTIFY_MODULE_CHANGED=$NOTIFY_MODULE_CHANGED" >> "$1" echo "WEB_3_WALLET_MODULE_CHANGED=$WEB_3_WALLET_MODULE_CHANGED" >> "$1" echo "WC_MODAL_MODULE_CHANGED=$WC_MODAL_MODULE_CHANGED" >> "$1" diff --git a/buildSrc/src/main/kotlin/VersionBump.kt b/buildSrc/src/main/kotlin/VersionBump.kt index 5f49c3cf2..5c3b95bd5 100644 --- a/buildSrc/src/main/kotlin/VersionBump.kt +++ b/buildSrc/src/main/kotlin/VersionBump.kt @@ -19,8 +19,8 @@ const val CHART_DELIMETER = "|" // note: Must match names in Version.kt enum class Version(var chartPosition: Int? = null) { - BOM(1), FOUNDATION(), CORE(2), SIGN(3), AUTH(4), CHAT(5), - NOTIFY(6), WEB_3_WALLET(7), WEB_3_MODAL(8), WC_MODAL(9), MODAL_CORE(); + BOM(1), FOUNDATION(), CORE(2), SIGN(3), + NOTIFY(4), WEB_3_WALLET(5), WEB_3_MODAL(6), WC_MODAL(7), MODAL_CORE(); val key: String = name + VERSION_SUFFIX } @@ -133,7 +133,7 @@ fun parseInput(properties: Map, inputType: InputType): Map parseManualInput(properties) } -// ./gradlew fixBump -Pmodules=FOUNDATION,CORE,SIGN,AUTH,CHAT,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE +// ./gradlew fixBump -Pmodules=FOUNDATION,CORE,SIGN,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE // ./gradlew releaseBump -Pmodules=FOUNDATION fun parseAutomaticInput(properties: Map): Map { val modules = properties[PROPERTY_MODULE_KEY]?.run(String::class::safeCast)?.run { this.uppercase().split(MODULE_SEPARATOR) } ?: throw Exception("No modules specified.") @@ -188,13 +188,12 @@ fun ensureModuleDependenciesWhenBumping(parsedVersions: Map): else -> { versions .run { if (this[Version.SIGN] == true) bumpSignDependantModules() else this } - .run { if (this[Version.AUTH] == true) bumpAuthDependantModules() else this } .run { if (this[Version.MODAL_CORE] == true) bumpModalCoreDependantModules() else this } } } } -// ./gradlew manualBump -PBOM=1.0.0 -PFOUNDATION=1.0.0 -PCORE=1.0.0 -PSIGN=1.0.0 -PAUTH=1.0.0 -PCHAT=1.0.0 -PNOTIFY=1.0.0 -PWEB_3_WALLET=1.0.0 -PWEB_3_MODAL=1.0.0 -PWC_MODAL=1.0.0 -PMODAL_CORE=1.0.0 +// ./gradlew manualBump -PBOM=1.0.0 -PFOUNDATION=1.0.0 -PCORE=1.0.0 -PSIGN=1.0.0 -PNOTIFY=1.0.0 -PWEB_3_WALLET=1.0.0 -PWEB_3_MODAL=1.0.0 -PWC_MODAL=1.0.0 -PMODAL_CORE=1.0.0 fun parseManualInput(properties: Map): Map { return Version.values().associate { version -> version to (properties[version.name]?.run(String::class::safeCast)?.run { true } ?: false) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 6d57b44a9..e00f7b501 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -5,29 +5,23 @@ const val KEY_PUBLISH_ARTIFACT_ID = "PUBLISH_ARTIFACT_ID" const val KEY_SDK_NAME = "SDK_NAME" //Latest versions -const val BOM_VERSION = "1.35.1" -const val FOUNDATION_VERSION = "1.18.3" -const val CORE_VERSION = "1.35.1" -const val SIGN_VERSION = "2.35.1" -const val AUTH_VERSION = "1.28.9" -const val CHAT_VERSION = "1.0.0-beta36" -const val NOTIFY_VERSION = "1.3.10" -const val WEB_3_WALLET_VERSION = "1.35.1" -const val WEB_3_MODAL_VERSION = "1.6.5" -const val WC_MODAL_VERSION = "1.5.10" -const val MODAL_CORE_VERSION = "1.6.5" +const val BOM_VERSION = "1.0.0" +const val FOUNDATION_VERSION = "1.0.0" +const val CORE_VERSION = "1.0.0" +const val SIGN_VERSION = "1.0.0" +const val NOTIFY_VERSION = "1.0.0" +const val WALLETKIT_VERSION = "1.0.0" +const val APPKIT_VERSION = "1.0.0" +const val MODAL_CORE_VERSION = "1.0.0" //Artifact ids const val ANDROID_BOM = "android-bom" const val FOUNDATION = "foundation" const val ANDROID_CORE = "android-core" const val SIGN = "sign" -const val AUTH = "auth" -const val CHAT = "chat" const val NOTIFY = "notify" -const val WEB_3_WALLET = "web3wallet" -const val WEB_3_MODAL = "web3modal" -const val WC_MODAL = "walletconnect-modal" +const val WALLETKIT = "walletkit" +const val APPKIT = "appkit" const val MODAL_CORE = "modal-core" val jvmVersion = JavaVersion.VERSION_11 diff --git a/buildSrc/src/main/kotlin/publish-module-android.gradle.kts b/buildSrc/src/main/kotlin/publish-module-android.gradle.kts index 098806a22..c4ff56577 100644 --- a/buildSrc/src/main/kotlin/publish-module-android.gradle.kts +++ b/buildSrc/src/main/kotlin/publish-module-android.gradle.kts @@ -36,14 +36,14 @@ afterEvaluate { artifact(tasks.getByName("javadocJar")) artifact(tasks.getByName("sourcesJar")) - groupId = "com.walletconnect" + groupId = "com.reown" artifactId = requireNotNull(project.extra[KEY_PUBLISH_ARTIFACT_ID]).toString() version = requireNotNull(project.extra[KEY_PUBLISH_VERSION]).toString() pom { - name.set("WalletConnect ${requireNotNull(extra.get(KEY_SDK_NAME))}") - description.set("${requireNotNull(extra.get(KEY_SDK_NAME))} SDK for WalletConnect") - url.set("https://github.com/WalletConnect/WalletConnectKotlinV2") + name.set("Reown ${requireNotNull(extra.get(KEY_SDK_NAME))}") + description.set("${requireNotNull(extra.get(KEY_SDK_NAME))} SDK for Reown") + url.set("https://github.com/reown-com/reown-kotlin") licenses { license { name.set("The Apache License, Version 2.0") @@ -58,15 +58,15 @@ afterEvaluate { developers { developer { id.set("KotlinSDKTeam") - name.set("WalletConnect Kotlin") - email.set("Kotlin@WalletConnect.com") + name.set("Reown Kotlin") + email.set("mobile@reown.com ") } } scm { - connection.set("scm:git:git://github.com/WalletConnect/WalletConnectKotlinV2.git") - developerConnection.set("scm:git:ssh://github.com/WalletConnect/WalletConnectKotlinV2.git") - url.set("https://github.com/WalletConnect/WalletConnectKotlinV2") + connection.set("scm:git:git://github.com/reown-com/reown-kotlin.git") + developerConnection.set("scm:git:ssh://github.com/reown-com/reown-kotlin.git") + url.set("https://github.com/reown-com/reown-kotlin") } } } diff --git a/buildSrc/src/main/kotlin/publish-module-java.gradle.kts b/buildSrc/src/main/kotlin/publish-module-java.gradle.kts index 3872dce24..76313552e 100644 --- a/buildSrc/src/main/kotlin/publish-module-java.gradle.kts +++ b/buildSrc/src/main/kotlin/publish-module-java.gradle.kts @@ -32,14 +32,14 @@ afterEvaluate { from(components["javaPlatform"]) } - groupId = "com.walletconnect" + groupId = "com.reown" artifactId = requireNotNull(extra.get(KEY_PUBLISH_ARTIFACT_ID)).toString() version = requireNotNull(extra.get(KEY_PUBLISH_VERSION)).toString() pom { - name.set("WalletConnect ${requireNotNull(extra.get(KEY_SDK_NAME))}") - description.set("${requireNotNull(extra.get(KEY_SDK_NAME))} SDK for WalletConnect") - url.set("https://github.com/WalletConnect/WalletConnectKotlinV2") + name.set("Reown ${requireNotNull(extra.get(KEY_SDK_NAME))}") + description.set("${requireNotNull(extra.get(KEY_SDK_NAME))} SDK for Reown") + url.set("https://github.com/reown-con/reown-kotlin") licenses { license { @@ -55,15 +55,15 @@ afterEvaluate { developers { developer { id.set("KotlinSDKTeam") - name.set("WalletConnect Kotlin") - email.set("Kotlin@WalletConnect.com") + name.set("Reown Kotlin") + email.set("mobile@reown.com ") } } scm { - connection.set("scm:git:git://github.com/WalletConnect/WalletConnectKotlinV2.git") - developerConnection.set("scm:git:ssh://github.com/WalletConnect/WalletConnectKotlinV2.git") - url.set("https://github.com/WalletConnect/WalletConnectKotlinV2") + connection.set("scm:git:git://github.com/reown-con/reown-kotlin.git") + developerConnection.set("scm:git:ssh://github.com/reown-con/reown-kotlin.git") + url.set("https://github.com/reown-con/reown-kotlin") } } } diff --git a/buildSrc/src/main/kotlin/release-scripts.gradle.kts b/buildSrc/src/main/kotlin/release-scripts.gradle.kts index 412226d0f..831e738ea 100644 --- a/buildSrc/src/main/kotlin/release-scripts.gradle.kts +++ b/buildSrc/src/main/kotlin/release-scripts.gradle.kts @@ -35,12 +35,9 @@ fun compileListOfSDKs(): List> = mutableListOf( Triple("core", "android", "android"), Triple("core", "modal", "android"), Triple("protocol", "sign", "android"), - Triple("protocol", "auth", "android"), - Triple("protocol", "chat", "android"), Triple("protocol", "notify", "android"), - Triple("product", "web3wallet", "android"), - Triple("product", "web3modal", "android"), - Triple("product", "walletconnectmodal", "android"), + Triple("product", "walletkit", "android"), + Triple("product", "appkit", "android") ).apply { // The BOM has to be last artifact add(Triple("core", "bom", "jvm")) diff --git a/buildSrc/src/main/kotlin/signing-config.gradle.kts b/buildSrc/src/main/kotlin/signing-config.gradle.kts index 781fa86c9..47ff5063b 100644 --- a/buildSrc/src/main/kotlin/signing-config.gradle.kts +++ b/buildSrc/src/main/kotlin/signing-config.gradle.kts @@ -52,7 +52,7 @@ project.extensions.configure(BaseExtension::class.java) { firebaseAppDistribution { artifactType = "AAB" serviceCredentialsFile = File(rootDir, "credentials.json").path - groups = "design-team, javascript-team, kotlin-team, rust-team, swift-team, wc-testers" + groups = "internal_testers" } } @@ -69,7 +69,7 @@ project.extensions.configure(BaseExtension::class.java) { firebaseAppDistribution { artifactType = "APK" serviceCredentialsFile = File(rootDir, "credentials.json").path - groups = "design-team, javascript-team, kotlin-team, rust-team, swift-team, wc-testers" + groups = "internal_testers" } } @@ -81,7 +81,7 @@ project.extensions.configure(BaseExtension::class.java) { firebaseAppDistribution { artifactType = "APK" serviceCredentialsFile = File(rootDir, "credentials.json").path - groups = "design-team, javascript-team, kotlin-team, rust-team, swift-team, wc-testers" + groups = "internal_testers" } } } diff --git a/buildSrc/src/main/kotlin/version-bump.gradle.kts b/buildSrc/src/main/kotlin/version-bump.gradle.kts index 0cce755a5..2f3e04698 100644 --- a/buildSrc/src/main/kotlin/version-bump.gradle.kts +++ b/buildSrc/src/main/kotlin/version-bump.gradle.kts @@ -25,7 +25,7 @@ tasks { } // Example usage: - // ./gradlew manualBump -PBOM=1.0.0 -PFOUNDATION=1.0.0 -PCORE=1.0.0 -PSIGN=1.0.0 -PAUTH=1.0.0 -PCHAT=1.0.0 -PNOTIFY=1.0.0 -PWEB_3_WALLET=1.0.0 -PWEB_3_MODAL=1.0.0 -PWC_MODAL=1.0.0 -PMODAL_CORE=1.0.0 + // ./gradlew manualBump -PBOM=1.0.0 -PFOUNDATION=1.0.0 -PCORE=1.0.0 -PSIGN=1.0.0 -PNOTIFY=1.0.0 -PWEB_3_WALLET=1.0.0 -PWEB_3_MODAL=1.0.0 -PWC_MODAL=1.0.0 -PMODAL_CORE=1.0.0 // ./gradlew manualBump -PNOTIFY=2.0.0 register("manualBump") { doLast { @@ -34,9 +34,9 @@ tasks { } // Example usage: - // ./gradlew releaseBump -Pmodules=FOUNDATION,CORE,SIGN,AUTH,CHAT,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE + // ./gradlew releaseBump -Pmodules=FOUNDATION,CORE,SIGN,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE // ./gradlew releaseBump -Pmodules=FOUNDATION - // ./gradlew releaseBump -Pmodules=AUTH,WEB_3_MODAL + // ./gradlew releaseBump -Pmodules=WEB_3_MODAL register("releaseBump") { doLast { writeFiles(bumpVersions(properties, VersionBumpType.RELEASE, InputType.AUTOMATIC)) @@ -44,9 +44,9 @@ tasks { } // Example usage: - // ./gradlew fixBump -Pmodules=FOUNDATION,CORE,SIGN,AUTH,CHAT,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE + // ./gradlew fixBump -Pmodules=FOUNDATION,CORE,SIGN,NOTIFY,WEB_3_WALLET,WEB_3_MODAL,WC_MODAL,MODAL_CORE // ./gradlew fixBump -Pmodules=FOUNDATION - // ./gradlew fixBump -Pmodules=AUTH,WEB_3_MODAL + // ./gradlew fixBump -Pmodules=WEB_3_MODAL register("fixBump") { doLast { writeFiles(bumpVersions(properties, VersionBumpType.FIX, InputType.AUTOMATIC)) diff --git a/core/android/ReadMe.md b/core/android/ReadMe.md index df3e19a9c..34de6018c 100644 --- a/core/android/ReadMe.md +++ b/core/android/ReadMe.md @@ -1,6 +1,6 @@ -# **WalletConnect Core - Android** +# **Core - Android** -Kotlin implementation of WalletConnect Core SDK for Android applications. +Kotlin implementation of reown Core SDK for Android applications. ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/android-core) @@ -33,7 +33,7 @@ allprojects { app/build.gradle.kts ```gradle -implementation("com.walletconnect:android-core:release_version") +implementation("com.reown:android-core:release_version") ``` ## Project ID diff --git a/core/android/build.gradle.kts b/core/android/build.gradle.kts index af8a69a5c..9932d36ca 100644 --- a/core/android/build.gradle.kts +++ b/core/android/build.gradle.kts @@ -14,7 +14,7 @@ project.apply { } android { - namespace = "com.walletconnect.android" + namespace = "com.reown.android" compileSdk = COMPILE_SDK defaultConfig { @@ -80,7 +80,7 @@ android { sqldelight { databases { create("AndroidCoreDatabase") { - packageName.set("com.walletconnect.android.sdk.core") + packageName.set("com.reown.android.sdk.core") schemaOutputDirectory.set(file("src/main/sqldelight/databases")) // generateAsync.set(true) // TODO: Enable once all repository methods have been converted to suspend functions verifyMigrations.set(true) @@ -90,7 +90,7 @@ sqldelight { dependencies { debugApi(project(":foundation")) - releaseApi("com.walletconnect:foundation:$FOUNDATION_VERSION") + releaseApi("com.reown:foundation:$FOUNDATION_VERSION") api(libs.coroutines) implementation(libs.scarlet.android) diff --git a/core/android/src/androidTest/AndroidManifest.xml b/core/android/src/androidTest/AndroidManifest.xml index 965bfffd7..6fc523c4a 100644 --- a/core/android/src/androidTest/AndroidManifest.xml +++ b/core/android/src/androidTest/AndroidManifest.xml @@ -7,7 +7,7 @@ diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/CacaoTest.kt b/core/android/src/androidTest/kotlin/com/reown/android/CacaoTest.kt similarity index 87% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/CacaoTest.kt rename to core/android/src/androidTest/kotlin/com/reown/android/CacaoTest.kt index dd8efa231..429438b6a 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/CacaoTest.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/CacaoTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.android +package com.reown.android -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.util.hexToBytes +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.android.utils.cacao.sign +import com.reown.util.hexToBytes import org.junit.Assert import org.junit.Ignore import org.junit.Test diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/LinkModeInteractorTests.kt b/core/android/src/androidTest/kotlin/com/reown/android/LinkModeInteractorTests.kt similarity index 89% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/LinkModeInteractorTests.kt rename to core/android/src/androidTest/kotlin/com/reown/android/LinkModeInteractorTests.kt index 314535021..ae6571475 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/LinkModeInteractorTests.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/LinkModeInteractorTests.kt @@ -1,19 +1,19 @@ -package com.walletconnect.android +package com.reown.android import android.content.Context import androidx.test.core.app.ApplicationProvider -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractor -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractor +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.wcKoinApp +import com.reown.foundation.common.model.Topic import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every diff --git a/core/android/src/androidTest/kotlin/com/reown/android/di/OverrideModule.kt b/core/android/src/androidTest/kotlin/com/reown/android/di/OverrideModule.kt new file mode 100644 index 000000000..dadd24f77 --- /dev/null +++ b/core/android/src/androidTest/kotlin/com/reown/android/di/OverrideModule.kt @@ -0,0 +1,34 @@ +package com.reown.android.di + +import com.reown.android.internal.common.di.coreCryptoModule +import com.reown.android.internal.common.di.coreJsonRpcModule +import com.reown.android.internal.common.di.corePairingModule +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.relay.RelayConnectionInterface +import org.koin.dsl.module + +private const val SHARED_PREFS_FILE = "wc_key_store" +private const val KEY_STORE_ALIAS = "wc_keystore_key" + +// When called more that once, different `storagePrefix` must be defined. +@JvmSynthetic +internal fun overrideModule( + relay: RelayConnectionInterface, + pairing: PairingInterface, + pairingController: PairingControllerInterface, + storagePrefix: String, + bundleId: String +) = module { + val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE + val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS + + single { relay } + + includes( + coreStorageModule(storagePrefix, bundleId), + corePairingModule(pairing, pairingController), + coreCryptoModule(sharedPrefsFile, keyStoreAlias), + coreJsonRpcModule() + ) +} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/MapperTest.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/MapperTest.kt similarity index 98% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/test/MapperTest.kt rename to core/android/src/androidTest/kotlin/com/reown/android/test/MapperTest.kt index 31b87c484..1619fa75f 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/MapperTest.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/MapperTest.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.test +package com.reown.android.test -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.toCAIP222Message import junit.framework.TestCase.assertEquals import org.junit.Test diff --git a/core/android/src/androidTest/kotlin/com/reown/android/test/activity/InstrumentedTestActivity.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/activity/InstrumentedTestActivity.kt new file mode 100644 index 000000000..414fac981 --- /dev/null +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/activity/InstrumentedTestActivity.kt @@ -0,0 +1,11 @@ +package com.reown.android.test.activity + +import android.app.Activity +import android.os.Bundle + +class InstrumentedTestActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/WCInstrumentedActivityScenario.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/activity/WCInstrumentedActivityScenario.kt similarity index 94% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/WCInstrumentedActivityScenario.kt rename to core/android/src/androidTest/kotlin/com/reown/android/test/activity/WCInstrumentedActivityScenario.kt index 72fea4d22..6d80ddd67 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/WCInstrumentedActivityScenario.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/activity/WCInstrumentedActivityScenario.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.test.activity +package com.reown.android.test.activity import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.test.utils.TestClient -import com.walletconnect.foundation.network.model.Relay +import com.reown.android.BuildConfig +import com.reown.android.internal.common.scope +import com.reown.android.test.utils.TestClient +import com.reown.foundation.network.model.Relay import junit.framework.TestCase.assertTrue import junit.framework.TestCase.fail import kotlinx.coroutines.CoroutineScope diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/client/KeyserverInstrumentedAndroidTest.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/client/KeyserverInstrumentedAndroidTest.kt similarity index 84% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/test/client/KeyserverInstrumentedAndroidTest.kt rename to core/android/src/androidTest/kotlin/com/reown/android/test/client/KeyserverInstrumentedAndroidTest.kt index 90506eee9..0a09b34e8 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/client/KeyserverInstrumentedAndroidTest.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/client/KeyserverInstrumentedAndroidTest.kt @@ -1,19 +1,19 @@ -package com.walletconnect.android.test.client +package com.reown.android.test.client -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.test.activity.WCInstrumentedActivityScenario -import com.walletconnect.android.test.utils.BNBAccount -import com.walletconnect.android.test.utils.EthereumAccount -import com.walletconnect.android.test.utils.SolanaAccount -import com.walletconnect.android.test.utils.TestClient -import com.walletconnect.android.test.utils.globalOnError -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey +import com.reown.android.BuildConfig +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.test.activity.WCInstrumentedActivityScenario +import com.reown.android.test.utils.BNBAccount +import com.reown.android.test.utils.EthereumAccount +import com.reown.android.test.utils.SolanaAccount +import com.reown.android.test.utils.TestClient +import com.reown.android.test.utils.globalOnError +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.android.utils.cacao.sign +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.util.jwt.encodeEd25519DidKey import org.junit.Rule import org.junit.Test import timber.log.Timber diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/ChainAccounts.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/ChainAccounts.kt similarity index 91% rename from core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/ChainAccounts.kt rename to core/android/src/androidTest/kotlin/com/reown/android/test/utils/ChainAccounts.kt index 412c3dd3f..bee5164b7 100644 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/ChainAccounts.kt +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/ChainAccounts.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.test.utils +package com.reown.android.test.utils -import com.walletconnect.foundation.common.model.PublicKey +import com.reown.foundation.common.model.PublicKey import io.ipfs.multibase.Multibase import org.web3j.crypto.Keys diff --git a/core/android/src/androidTest/kotlin/com/reown/android/test/utils/Common.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/Common.kt new file mode 100644 index 000000000..9f93fed87 --- /dev/null +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/Common.kt @@ -0,0 +1,15 @@ +package com.reown.android.test.utils + +import com.reown.android.Core +import junit.framework.TestCase.fail +import timber.log.Timber + +internal fun globalOnError(error: Core.Model.Error) { + Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") + fail(error.throwable.message) +} + +internal fun globalOnError(error: Throwable) { + Timber.e("globalOnError: ${error.stackTraceToString()}") + fail(error.message) +} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/reown/android/test/utils/TestClient.kt b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/TestClient.kt new file mode 100644 index 000000000..c408aa672 --- /dev/null +++ b/core/android/src/androidTest/kotlin/com/reown/android/test/utils/TestClient.kt @@ -0,0 +1,98 @@ +package com.reown.android.test.utils + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import com.reown.android.BuildConfig +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.CoreProtocol +import com.reown.android.di.overrideModule +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.model.type.JsonRpcInteractorInterface +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayClient +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.koin.core.KoinApplication +import org.koin.core.qualifier.named +import timber.log.Timber + +internal object TestClient { + private val app = ApplicationProvider.getApplicationContext() + fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = KoinApplication.init().apply { createEagerInstances() } + + object Primary { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Primary", + description = "Primary client for automation tests", + url = "kotlin.e2e.primary", + icons = listOf(), + redirect = null + ) + + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + + private val coreProtocol = CoreClient.apply { + Timber.d("Primary CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) + Relay.connect(::globalOnError) + _isInitialized.tryEmit(true) + Timber.d("Primary CP finish: ") + } + + + internal val Relay get() = coreProtocol.Relay + internal val jsonRpcInteractor: RelayJsonRpcInteractorInterface by lazy { wcKoinApp.koin.get() } + internal val keyManagementRepository: KeyManagementRepository by lazy { wcKoinApp.koin.get() } + internal val identitiesInteractor: IdentitiesInteractor by lazy { wcKoinApp.koin.get() } + internal val keyserverUrl: String by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.KEYSERVER_URL)) } + } + + + object Secondary { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Secondary", + description = "Secondary client for automation tests", + url = "kotlin.e2e.secondary", + icons = listOf(), + redirect = null + ) + + private val secondaryKoinApp = KoinApplication.createNewWCKoinApp() + + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + + private val coreProtocol = CoreProtocol(secondaryKoinApp).apply { + Timber.d("Secondary CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } + + // Override of previous Relay necessary for reinitialization of `eventsFlow` + Relay = RelayClient(secondaryKoinApp) + + // Override of storage instances and depending objects + secondaryKoinApp.modules(overrideModule(Relay, Pairing, PairingController, "test_secondary", app.packageName)) + + // Necessary reinit of Relay, Pairing and PairingController + Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } + Pairing.initialize() + PairingController.initialize() + + Relay.connect(::globalOnError) + _isInitialized.tryEmit(true) + Timber.d("Secondary CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val jsonRpcInteractor: RelayJsonRpcInteractorInterface by lazy { secondaryKoinApp.koin.get() } + internal val keyManagementRepository: KeyManagementRepository by lazy { secondaryKoinApp.koin.get() } + + } +} diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/di/OverrideModule.kt b/core/android/src/androidTest/kotlin/com/walletconnect/android/di/OverrideModule.kt deleted file mode 100644 index c01c0a28e..000000000 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/di/OverrideModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.walletconnect.android.di - -import com.walletconnect.android.internal.common.di.coreCryptoModule -import com.walletconnect.android.internal.common.di.coreJsonRpcModule -import com.walletconnect.android.internal.common.di.corePairingModule -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.relay.RelayConnectionInterface -import org.koin.dsl.module - -private const val SHARED_PREFS_FILE = "wc_key_store" -private const val KEY_STORE_ALIAS = "wc_keystore_key" - -// When called more that once, different `storagePrefix` must be defined. -@JvmSynthetic -internal fun overrideModule( - relay: RelayConnectionInterface, - pairing: PairingInterface, - pairingController: PairingControllerInterface, - storagePrefix: String, - bundleId: String -) = module { - val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE - val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS - - single { relay } - - includes( - coreStorageModule(storagePrefix, bundleId), - corePairingModule(pairing, pairingController), - coreCryptoModule(sharedPrefsFile, keyStoreAlias), - coreJsonRpcModule() - ) -} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/InstrumentedTestActivity.kt b/core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/InstrumentedTestActivity.kt deleted file mode 100644 index df391d064..000000000 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/activity/InstrumentedTestActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.android.test.activity - -import android.app.Activity -import android.os.Bundle - -class InstrumentedTestActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } -} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/Common.kt b/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/Common.kt deleted file mode 100644 index 8a2e94682..000000000 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/Common.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.android.test.utils - -import com.walletconnect.android.Core -import junit.framework.TestCase.fail -import timber.log.Timber - -internal fun globalOnError(error: Core.Model.Error) { - Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") - fail(error.throwable.message) -} - -internal fun globalOnError(error: Throwable) { - Timber.e("globalOnError: ${error.stackTraceToString()}") - fail(error.message) -} \ No newline at end of file diff --git a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/TestClient.kt b/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/TestClient.kt deleted file mode 100644 index 3f43463e6..000000000 --- a/core/android/src/androidTest/kotlin/com/walletconnect/android/test/utils/TestClient.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.walletconnect.android.test.utils - -import android.app.Application -import androidx.test.core.app.ApplicationProvider -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.CoreProtocol -import com.walletconnect.android.di.overrideModule -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.model.type.JsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayClient -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import org.koin.core.KoinApplication -import org.koin.core.qualifier.named -import timber.log.Timber - -internal object TestClient { - private val app = ApplicationProvider.getApplicationContext() - fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = KoinApplication.init().apply { createEagerInstances() } - - object Primary { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Primary", - description = "Primary client for automation tests", - url = "kotlin.e2e.primary", - icons = listOf(), - redirect = null - ) - - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - - private val coreProtocol = CoreClient.apply { - Timber.d("Primary CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) - Relay.connect(::globalOnError) - _isInitialized.tryEmit(true) - Timber.d("Primary CP finish: ") - } - - - internal val Relay get() = coreProtocol.Relay - internal val jsonRpcInteractor: RelayJsonRpcInteractorInterface by lazy { wcKoinApp.koin.get() } - internal val keyManagementRepository: KeyManagementRepository by lazy { wcKoinApp.koin.get() } - internal val identitiesInteractor: IdentitiesInteractor by lazy { wcKoinApp.koin.get() } - internal val keyserverUrl: String by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.KEYSERVER_URL)) } - } - - - object Secondary { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Secondary", - description = "Secondary client for automation tests", - url = "kotlin.e2e.secondary", - icons = listOf(), - redirect = null - ) - - private val secondaryKoinApp = KoinApplication.createNewWCKoinApp() - - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - - private val coreProtocol = CoreProtocol(secondaryKoinApp).apply { - Timber.d("Secondary CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } - - // Override of previous Relay necessary for reinitialization of `eventsFlow` - Relay = RelayClient(secondaryKoinApp) - - // Override of storage instances and depending objects - secondaryKoinApp.modules(overrideModule(Relay, Pairing, PairingController, "test_secondary", app.packageName)) - - // Necessary reinit of Relay, Pairing and PairingController - Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } - Pairing.initialize() - PairingController.initialize() - - Relay.connect(::globalOnError) - _isInitialized.tryEmit(true) - Timber.d("Secondary CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val jsonRpcInteractor: RelayJsonRpcInteractorInterface by lazy { secondaryKoinApp.koin.get() } - internal val keyManagementRepository: KeyManagementRepository by lazy { secondaryKoinApp.koin.get() } - - } -} diff --git a/core/android/src/debug/kotlin/com/reown/android/di/AndroidBuildVariantDITags.kt b/core/android/src/debug/kotlin/com/reown/android/di/AndroidBuildVariantDITags.kt new file mode 100644 index 000000000..ae9ce236a --- /dev/null +++ b/core/android/src/debug/kotlin/com/reown/android/di/AndroidBuildVariantDITags.kt @@ -0,0 +1,6 @@ +package com.reown.android.di + +enum class AndroidBuildVariantDITags { + ANDROID_CORE_DATABASE_DRIVER, + ANDROID_CORE_DATABASE, +} \ No newline at end of file diff --git a/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt b/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt new file mode 100644 index 000000000..1e7722309 --- /dev/null +++ b/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt @@ -0,0 +1,36 @@ +package com.reown.android.di + +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import com.reown.android.internal.common.di.DatabaseConfig +import com.reown.android.internal.common.di.baseStorageModule +import com.reown.android.sdk.core.AndroidCoreDatabase +import com.reown.utils.Empty +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module + +fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { + + includes(baseStorageModule(storagePrefix, bundleId)) + + single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { + AndroidSqliteDriver( + schema = AndroidCoreDatabase.Schema, + context = androidContext(), + name = get().ANDROID_CORE_DB_NAME, + ) + } +} + +fun sdkBaseStorageModule(databaseSchema: SqlSchema>, databaseName: String) = module { + single(named(databaseName)) { + AndroidSqliteDriver( + schema = databaseSchema, + context = androidContext(), + name = databaseName, + ) + } +} \ No newline at end of file diff --git a/core/android/src/debug/kotlin/com/walletconnect/android/di/AndroidBuildVariantDITags.kt b/core/android/src/debug/kotlin/com/walletconnect/android/di/AndroidBuildVariantDITags.kt deleted file mode 100644 index af01b3e5c..000000000 --- a/core/android/src/debug/kotlin/com/walletconnect/android/di/AndroidBuildVariantDITags.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.di - -enum class AndroidBuildVariantDITags { - ANDROID_CORE_DATABASE_DRIVER, - ANDROID_CORE_DATABASE, -} \ No newline at end of file diff --git a/core/android/src/debug/kotlin/com/walletconnect/android/di/CoreStorageModule.kt b/core/android/src/debug/kotlin/com/walletconnect/android/di/CoreStorageModule.kt deleted file mode 100644 index 53084c3ba..000000000 --- a/core/android/src/debug/kotlin/com/walletconnect/android/di/CoreStorageModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.walletconnect.android.di - -import app.cash.sqldelight.db.QueryResult -import app.cash.sqldelight.db.SqlDriver -import app.cash.sqldelight.db.SqlSchema -import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import com.walletconnect.android.internal.common.di.DatabaseConfig -import com.walletconnect.android.internal.common.di.baseStorageModule -import com.walletconnect.android.sdk.core.AndroidCoreDatabase -import com.walletconnect.utils.Empty -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.dsl.module - -fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { - - includes(baseStorageModule(storagePrefix, bundleId)) - - single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { - AndroidSqliteDriver( - schema = AndroidCoreDatabase.Schema, - context = androidContext(), - name = get().ANDROID_CORE_DB_NAME, - ) - } -} - -fun sdkBaseStorageModule(databaseSchema: SqlSchema>, databaseName: String) = module { - single(named(databaseName)) { - AndroidSqliteDriver( - schema = databaseSchema, - context = androidContext(), - name = databaseName, - ) - } -} \ No newline at end of file diff --git a/core/android/src/main/AndroidManifest.xml b/core/android/src/main/AndroidManifest.xml index a6b340077..095d9237b 100644 --- a/core/android/src/main/AndroidManifest.xml +++ b/core/android/src/main/AndroidManifest.xml @@ -1,11 +1,11 @@ + package="com.reown.android"> - - - + + + diff --git a/core/android/src/main/kotlin/com/walletconnect/android/Core.kt b/core/android/src/main/kotlin/com/reown/android/Core.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/Core.kt rename to core/android/src/main/kotlin/com/reown/android/Core.kt index a4ad20b32..cc07258df 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/Core.kt +++ b/core/android/src/main/kotlin/com/reown/android/Core.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android +package com.reown.android -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry object Core { sealed interface Listeners { @@ -132,29 +132,6 @@ object Core { val iat: String ) : Model() } - - @Deprecated("Use SessionAuthenticate instead") - data class AuthRequest( - val id: Long, - val pairingTopic: String, - val metadata: AppMetaData, - val payloadParams: PayloadParams, - ) : Message() { - data class PayloadParams( - val type: String, - val chainId: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) - } } } diff --git a/core/android/src/main/kotlin/com/walletconnect/android/CoreClient.kt b/core/android/src/main/kotlin/com/reown/android/CoreClient.kt similarity index 77% rename from core/android/src/main/kotlin/com/walletconnect/android/CoreClient.kt rename to core/android/src/main/kotlin/com/reown/android/CoreClient.kt index f6f8c15ef..4f80fc63c 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/CoreClient.kt +++ b/core/android/src/main/kotlin/com/reown/android/CoreClient.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android +package com.reown.android object CoreClient : CoreInterface by CoreProtocol.instance { diff --git a/core/android/src/main/kotlin/com/reown/android/CoreInterface.kt b/core/android/src/main/kotlin/com/reown/android/CoreInterface.kt new file mode 100644 index 000000000..3a8f6cdc9 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/CoreInterface.kt @@ -0,0 +1,50 @@ +package com.reown.android + +import android.app.Application +import com.reown.android.internal.common.explorer.ExplorerInterface +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.push.PushInterface +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.NetworkClientTimeout +import com.reown.android.relay.RelayConnectionInterface +import com.reown.android.verify.client.VerifyInterface + +interface CoreInterface { + val Pairing: PairingInterface + val PairingController: PairingControllerInterface + val Relay: RelayConnectionInterface + @Deprecated(message = "Replaced with Push") + val Echo: PushInterface + val Push: PushInterface + val Verify: VerifyInterface + val Explorer: ExplorerInterface + + interface Delegate : PairingInterface.Delegate + + fun setDelegate(delegate: Delegate) + + fun initialize( + metaData: Core.Model.AppMetaData, + relayServerUrl: String, + connectionType: ConnectionType = ConnectionType.AUTOMATIC, + application: Application, + relay: RelayConnectionInterface? = null, + keyServerUrl: String? = null, + networkClientTimeout: NetworkClientTimeout? = null, + telemetryEnabled: Boolean = true, + onError: (Core.Model.Error) -> Unit, + ) + + fun initialize( + application: Application, + projectId: String, + metaData: Core.Model.AppMetaData, + connectionType: ConnectionType = ConnectionType.AUTOMATIC, + relay: RelayConnectionInterface? = null, + keyServerUrl: String? = null, + networkClientTimeout: NetworkClientTimeout? = null, + telemetryEnabled: Boolean = true, + onError: (Core.Model.Error) -> Unit, + ) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt b/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt new file mode 100644 index 000000000..4e3996dc0 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt @@ -0,0 +1,192 @@ +package com.reown.android + +import android.app.Application +import android.content.SharedPreferences +import com.reown.android.di.coreStorageModule +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.di.KEY_CLIENT_ID +import com.reown.android.internal.common.di.coreAndroidNetworkModule +import com.reown.android.internal.common.di.coreCommonModule +import com.reown.android.internal.common.di.coreCryptoModule +import com.reown.android.internal.common.di.coreJsonRpcModule +import com.reown.android.internal.common.di.corePairingModule +import com.reown.android.internal.common.di.explorerModule +import com.reown.android.internal.common.di.keyServerModule +import com.reown.android.internal.common.di.pulseModule +import com.reown.android.internal.common.di.pushModule +import com.reown.android.internal.common.di.appKitModule +import com.reown.android.internal.common.explorer.ExplorerInterface +import com.reown.android.internal.common.explorer.ExplorerProtocol +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.model.Redirect +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.client.PairingProtocol +import com.reown.android.pairing.handler.PairingController +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.push.PushInterface +import com.reown.android.push.client.PushClient +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.NetworkClientTimeout +import com.reown.android.relay.RelayClient +import com.reown.android.relay.RelayConnectionInterface +import com.reown.android.utils.isValidRelayServerUrl +import com.reown.android.utils.plantTimber +import com.reown.android.utils.projectId +import com.reown.android.verify.client.VerifyClient +import com.reown.android.verify.client.VerifyInterface +import org.koin.android.ext.koin.androidContext +import org.koin.core.KoinApplication +import org.koin.core.qualifier.named +import org.koin.dsl.module + +class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInterface { + override val Pairing: PairingInterface = PairingProtocol(koinApp) + override val PairingController: PairingControllerInterface = PairingController(koinApp) + override var Relay = RelayClient(koinApp) + + @Deprecated(message = "Replaced with Push") + override val Echo: PushInterface = PushClient + override val Push: PushInterface = PushClient + override val Verify: VerifyInterface = VerifyClient(koinApp) + override val Explorer: ExplorerInterface = ExplorerProtocol(koinApp) + + init { + plantTimber() + } + + override fun setDelegate(delegate: CoreInterface.Delegate) { + Pairing.setDelegate(delegate) + } + + companion object { + val instance = CoreProtocol() + } + + override fun initialize( + metaData: Core.Model.AppMetaData, + relayServerUrl: String, + connectionType: ConnectionType, + application: Application, + relay: RelayConnectionInterface?, + keyServerUrl: String?, + networkClientTimeout: NetworkClientTimeout?, + telemetryEnabled: Boolean, + onError: (Core.Model.Error) -> Unit + ) { + try { + require(relayServerUrl.isValidRelayServerUrl()) { "Check the schema and projectId parameter of the Server Url" } + + setup( + application = application, + serverUrl = relayServerUrl, + projectId = relayServerUrl.projectId(), + telemetryEnabled = telemetryEnabled, + connectionType = connectionType, + networkClientTimeout = networkClientTimeout, + relay = relay, + onError = onError, + metaData = metaData, + keyServerUrl = keyServerUrl + ) + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + override fun initialize( + application: Application, + projectId: String, + metaData: Core.Model.AppMetaData, + connectionType: ConnectionType, + relay: RelayConnectionInterface?, + keyServerUrl: String?, + networkClientTimeout: NetworkClientTimeout?, + telemetryEnabled: Boolean, + onError: (Core.Model.Error) -> Unit + ) { + try { + require(projectId.isNotEmpty()) { "Project Id cannot be empty" } + + setup( + application = application, + projectId = projectId, + telemetryEnabled = telemetryEnabled, + connectionType = connectionType, + networkClientTimeout = networkClientTimeout, + relay = relay, + onError = onError, + metaData = metaData, + keyServerUrl = keyServerUrl + ) + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + private fun CoreProtocol.setup( + application: Application, + serverUrl: String? = null, + projectId: String, + telemetryEnabled: Boolean, + connectionType: ConnectionType, + networkClientTimeout: NetworkClientTimeout?, + relay: RelayConnectionInterface?, + onError: (Core.Model.Error) -> Unit, + metaData: Core.Model.AppMetaData, + keyServerUrl: String? + ) { + val bundleId: String = application.packageName + val relayServerUrl = if (serverUrl.isNullOrEmpty()) "wss://relay.walletconnect.org?projectId=$projectId" else serverUrl + + with(koinApp) { + androidContext(application) + modules( + module { single { ProjectId(projectId) } }, + module { single(named(AndroidCommonDITags.TELEMETRY_ENABLED)) { TelemetryEnabled(telemetryEnabled) } }, + coreAndroidNetworkModule(relayServerUrl, connectionType, BuildConfig.SDK_VERSION, networkClientTimeout, bundleId), + coreCommonModule(), + coreCryptoModule(), + ) + + if (relay == null) { + Relay.initialize(connectionType) { error -> onError(Core.Model.Error(error)) } + } + + modules( + coreStorageModule(bundleId = bundleId), + module { single(named(AndroidCommonDITags.CLIENT_ID)) { requireNotNull(get().getString(KEY_CLIENT_ID, null)) } }, + pushModule(), + module { single { relay ?: Relay } }, + module { + single { + with(metaData) { + AppMetaData( + name = name, + description = description, + url = url, + icons = icons, + redirect = Redirect(native = redirect, universal = appLink, linkMode = linkMode) + ) + } + } + }, + module { single { Echo } }, + module { single { Push } }, + module { single { Verify } }, + coreJsonRpcModule(), + corePairingModule(Pairing, PairingController), + keyServerModule(keyServerUrl), + explorerModule(), + appKitModule(), + pulseModule(bundleId) + ) + } + + Pairing.initialize() + PairingController.initialize() + Verify.initialize() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/Messages.kt b/core/android/src/main/kotlin/com/reown/android/internal/Messages.kt new file mode 100644 index 000000000..705b568be --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/Messages.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal + +@JvmSynthetic +internal const val MALFORMED_PAIRING_URI_MESSAGE: String = "Pairing URI string is invalid." + +@JvmSynthetic +internal const val PAIRING_NOT_ALLOWED_MESSAGE: String = "Pairing already exists. Please try again with a new connection URI" + +@JvmSynthetic +internal const val NO_SEQUENCE_FOR_TOPIC_MESSAGE: String = "Cannot find sequence for given topic: " \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/Proposal.kt b/core/android/src/main/kotlin/com/reown/android/internal/Proposal.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/Proposal.kt rename to core/android/src/main/kotlin/com/reown/android/internal/Proposal.kt index a2fa6da3d..ddd462d81 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/Proposal.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/Proposal.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal +package com.reown.android.internal import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/Validator.kt b/core/android/src/main/kotlin/com/reown/android/internal/Validator.kt similarity index 85% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/Validator.kt rename to core/android/src/main/kotlin/com/reown/android/internal/Validator.kt index f571bef80..c2bc808df 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/Validator.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/Validator.kt @@ -1,12 +1,12 @@ @file:JvmSynthetic -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.WalletConnectUri -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.WalletConnectUri +import com.reown.foundation.common.model.Topic import java.net.URI import java.net.URISyntaxException import java.net.URLDecoder diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/ConditionalExponentialBackoffStrategy.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/ConditionalExponentialBackoffStrategy.kt new file mode 100644 index 000000000..7f071b82b --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/ConditionalExponentialBackoffStrategy.kt @@ -0,0 +1,23 @@ +package com.reown.android.internal.common + +import com.tinder.scarlet.retry.BackoffStrategy +import kotlin.math.pow + +class ConditionalExponentialBackoffStrategy( + private val initialDurationMillis: Long, + private val maxDurationMillis: Long +) : BackoffStrategy { + init { + require(initialDurationMillis > 0) { "initialDurationMillis, $initialDurationMillis, must be positive" } + require(maxDurationMillis > 0) { "maxDurationMillis, $maxDurationMillis, must be positive" } + } + + override var shouldBackoff: Boolean = false + + fun shouldBackoff(shouldBackoff: Boolean) { + this.shouldBackoff = shouldBackoff + } + + override fun backoffDurationMillisAt(retryCount: Int): Long = + maxDurationMillis.toDouble().coerceAtMost(initialDurationMillis.toDouble() * 2.0.pow(retryCount.toDouble())).toLong() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/JsonRpcResponse.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/JsonRpcResponse.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/JsonRpcResponse.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/JsonRpcResponse.kt index 7287971a6..ed7f23760 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/JsonRpcResponse.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/JsonRpcResponse.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.internal.common +package com.reown.android.internal.common import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.SerializableJsonRpc +import com.reown.android.internal.common.model.type.SerializableJsonRpc sealed class JsonRpcResponse : SerializableJsonRpc { abstract val id: Long diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/KoinApplication.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/KoinApplication.kt new file mode 100644 index 000000000..c32840a0c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/KoinApplication.kt @@ -0,0 +1,5 @@ +package com.reown.android.internal.common + +import org.koin.core.KoinApplication + +var wcKoinApp: KoinApplication = KoinApplication.init().apply { createEagerInstances() } \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/WalletConnectScope.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/WalletConnectScope.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/WalletConnectScope.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/WalletConnectScope.kt index d263c4316..bfc0350c9 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/WalletConnectScope.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/WalletConnectScope.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common +package com.reown.android.internal.common import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapter.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapter.kt similarity index 87% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapter.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapter.kt index 6351dcbf5..0c51bd171 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapter.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapter.kt @@ -1,9 +1,9 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.adapter +package com.reown.android.internal.common.adapter import com.squareup.moshi.* -import com.walletconnect.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Expiry internal object ExpiryAdapter: JsonAdapter() { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/JsonRpcResultAdapter.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/JsonRpcResultAdapter.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/JsonRpcResultAdapter.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/adapter/JsonRpcResultAdapter.kt index 5a26ead00..e68e41cbb 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/JsonRpcResultAdapter.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/JsonRpcResultAdapter.kt @@ -1,14 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.adapter +package com.reown.android.internal.common.adapter import com.squareup.moshi.* import com.squareup.moshi.internal.Util -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.params.CoreSignParams import org.json.JSONArray import org.json.JSONObject import java.lang.reflect.Constructor @@ -26,8 +25,6 @@ internal class JsonRpcResultAdapter(val moshi: Moshi) : JsonAdapter = moshi.adapter(CoreSignParams.SessionAuthenticateApproveParams::class.java, emptySet(), "result") - private val cacaoAdapter: JsonAdapter = - moshi.adapter(CoreAuthParams.ResponseParams::class.java, emptySet(), "result") private val notifySubscribeUpdateParamsAdapter: JsonAdapter = moshi.adapter(CoreNotifyParams.UpdateParams::class.java, emptySet(), "result") private val chatNotifyResponseAuthParamsAdapter: JsonAdapter = @@ -68,11 +65,6 @@ internal class JsonRpcResultAdapter(val moshi: Moshi) : JsonAdapter { - cacaoAdapter.fromJson(reader) - } - runCatching { notifySubscribeUpdateParamsAdapter.fromJson(reader.peekJson()) }.isSuccess -> { notifySubscribeUpdateParamsAdapter.fromJson(reader) } @@ -154,14 +146,6 @@ internal class JsonRpcResultAdapter(val moshi: Moshi) : JsonAdapter { - val responseParamsString = - cacaoAdapter.toJson(value_.result) - writer.valueSink().use { - it.writeUtf8(responseParamsString) - } - } - (value_.result as? ChatNotifyResponseAuthParams.ResponseAuth) != null -> { val notifySubscribeResponseParamsString = chatNotifyResponseAuthParamsAdapter.toJson(value_.result) writer.valueSink().use { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/TagsAdapter.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/TagsAdapter.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/TagsAdapter.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/adapter/TagsAdapter.kt index cef3ffa55..11311600d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/adapter/TagsAdapter.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/adapter/TagsAdapter.kt @@ -1,9 +1,9 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.adapter +package com.reown.android.internal.common.adapter import com.squareup.moshi.* -import com.walletconnect.android.internal.common.model.Tags +import com.reown.android.internal.common.model.Tags internal object TagsAdapter : JsonAdapter() { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ConnectivityState.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/ConnectivityState.kt similarity index 97% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ConnectivityState.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/connection/ConnectivityState.kt index faea4a542..9bb775ebb 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ConnectivityState.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/ConnectivityState.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.connection +package com.reown.android.internal.common.connection import android.annotation.SuppressLint import android.content.Context diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/connection/DefaultConnectionLifecycle.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/DefaultConnectionLifecycle.kt new file mode 100644 index 000000000..031ed4b72 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/DefaultConnectionLifecycle.kt @@ -0,0 +1,83 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.connection + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import com.tinder.scarlet.Lifecycle +import com.tinder.scarlet.ShutdownReason +import com.tinder.scarlet.lifecycle.LifecycleRegistry +import com.reown.foundation.network.ConnectionLifecycle +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import java.util.concurrent.TimeUnit + +internal class DefaultConnectionLifecycle( + application: Application, + private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry() +) : Lifecycle by lifecycleRegistry, ConnectionLifecycle { + private val job = SupervisorJob() + private var scope = CoroutineScope(job + Dispatchers.Default) + + private val _onResume = MutableStateFlow(null) + override val onResume: StateFlow = _onResume.asStateFlow() + + init { + application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks()) + } + + override fun reconnect() { + lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason()) + lifecycleRegistry.onNext(Lifecycle.State.Started) + } + + private inner class ActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks { + var isResumed: Boolean = false + var job: Job? = null + + override fun onActivityPaused(activity: Activity) { + isResumed = false + + job = scope.launch { + delay(TimeUnit.SECONDS.toMillis(30)) + if (!isResumed) { + lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason(ShutdownReason(1000, "App is paused"))) + job = null + _onResume.value = false + } + } + } + + override fun onActivityResumed(activity: Activity) { + isResumed = true + + if (job?.isActive == true) { + job?.cancel() + job = null + } + + + scope.launch { + _onResume.value = true + } + } + + override fun onActivityStarted(activity: Activity) {} + + override fun onActivityDestroyed(activity: Activity) {} + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + + override fun onActivityStopped(activity: Activity) {} + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/connection/ManualConnectionLifecycle.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/ManualConnectionLifecycle.kt new file mode 100644 index 000000000..0d562dcec --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/connection/ManualConnectionLifecycle.kt @@ -0,0 +1,29 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.connection + +import com.tinder.scarlet.Lifecycle +import com.tinder.scarlet.lifecycle.LifecycleRegistry +import com.reown.foundation.network.ConnectionLifecycle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +internal class ManualConnectionLifecycle( + private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(), +) : Lifecycle by lifecycleRegistry, ConnectionLifecycle { + fun connect() { + lifecycleRegistry.onNext(Lifecycle.State.Started) + } + + fun disconnect() { + lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason()) + } + + override val onResume: StateFlow + get() = MutableStateFlow(null) + + override fun reconnect() { + lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason()) + lifecycleRegistry.onNext(Lifecycle.State.Started) + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/Utils.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/Utils.kt new file mode 100644 index 000000000..ca9dd0c7c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/Utils.kt @@ -0,0 +1,14 @@ +package com.reown.android.internal.common.crypto + +import com.reown.util.bytesToHex +import java.security.MessageDigest + + +private const val SHA_256: String = "SHA-256" + +fun sha256(input: ByteArray): String { + val messageDigest: MessageDigest = MessageDigest.getInstance(SHA_256) + val hashedBytes: ByteArray = messageDigest.digest(input) + + return hashedBytes.bytesToHex() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/ChaChaPolyCodec.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/ChaChaPolyCodec.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/ChaChaPolyCodec.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/ChaChaPolyCodec.kt index 7a1f42789..4ea9532c2 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/ChaChaPolyCodec.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/ChaChaPolyCodec.kt @@ -1,20 +1,20 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.crypto.codec - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.MissingParticipantsException -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.UnknownEnvelopeTypeException -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes -import com.walletconnect.util.randomBytes +package com.reown.android.internal.common.crypto.codec + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.MissingKeyException +import com.reown.android.internal.common.model.MissingParticipantsException +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.UnknownEnvelopeTypeException +import com.reown.android.internal.utils.getParticipantTag +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes +import com.reown.util.randomBytes import org.bouncycastle.crypto.modes.ChaCha20Poly1305 import org.bouncycastle.crypto.params.KeyParameter import org.bouncycastle.crypto.params.ParametersWithIV diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/Codec.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/Codec.kt new file mode 100644 index 000000000..936ecb483 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/codec/Codec.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal.common.crypto.codec + +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.Participants +import com.reown.foundation.common.model.Topic + +interface Codec { + fun encrypt(topic: Topic, payload: String, envelopeType: EnvelopeType, participants: Participants? = null): ByteArray + fun decrypt(topic: Topic, cipherText: ByteArray): String +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt index 375bcdb77..e6725a330 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/BouncyCastleKeyManagementRepository.kt @@ -1,17 +1,17 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.crypto.kmr - -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore -import com.walletconnect.foundation.common.model.Key -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes +package com.reown.android.internal.common.crypto.kmr + +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.MissingKeyException +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.storage.key_chain.KeyStore +import com.reown.foundation.common.model.Key +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes import org.bouncycastle.crypto.digests.SHA256Digest import org.bouncycastle.crypto.generators.HKDFBytesGenerator import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/KeyManagementRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/KeyManagementRepository.kt new file mode 100644 index 000000000..1bc1c69b4 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/crypto/kmr/KeyManagementRepository.kt @@ -0,0 +1,32 @@ +package com.reown.android.internal.common.crypto.kmr + +import com.reown.android.internal.common.model.MissingKeyException +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.foundation.common.model.Key +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic + +interface KeyManagementRepository { + fun setKey(key: Key, tag: String) + @Throws(MissingKeyException::class) + fun removeKeys(tag: String) + + fun getPublicKey(tag: String): PublicKey + fun getSymmetricKey(tag: String): SymmetricKey + fun getKeyPair(key: PublicKey): Pair + fun setKeyPair(publicKey: PublicKey, privateKey: PrivateKey) + fun generateAndStoreEd25519KeyPair(): PublicKey + + fun deriveAndStoreEd25519KeyPair(privateKey: PrivateKey): PublicKey + + fun generateAndStoreX25519KeyPair(): PublicKey + fun setKeyAgreement(topic: Topic, self: PublicKey, peer: PublicKey) + fun getSelfPublicFromKeyAgreement(topic: Topic): PublicKey + + fun generateAndStoreSymmetricKey(topic: Topic): SymmetricKey + fun generateSymmetricKeyFromKeyAgreement(self: PublicKey, peer: PublicKey): SymmetricKey + + fun getTopicFromKey(key: Key): Topic + fun generateTopicFromKeyAgreement(self: PublicKey, peer: PublicKey): Topic +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/AndroidCommonDITags.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/AndroidCommonDITags.kt similarity index 83% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/AndroidCommonDITags.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/AndroidCommonDITags.kt index 2fcb45460..eb4b4cdca 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/AndroidCommonDITags.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/AndroidCommonDITags.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di enum class AndroidCommonDITags { MOSHI, @@ -10,7 +10,7 @@ enum class AndroidCommonDITags { SCARLET, MSG_ADAPTER, MANUAL_CONNECTION_LIFECYCLE, - AUTOMATIC_CONNECTION_LIFECYCLE, + DEFAULT_CONNECTION_LIFECYCLE, LOGGER, CONNECTIVITY_STATE, PUSH_RETROFIT, @@ -32,9 +32,9 @@ enum class AndroidCommonDITags { COLUMN_ADAPTER_APPMETADATATYPE, COLUMN_ADAPTER_TRANSPORT_TYPE, WEB3MODAL_URL, - WEB3MODAL_INTERCEPTOR, - WEB3MODAL_OKHTTP, - WEB3MODAL_RETROFIT, + APPKIT_INTERCEPTOR, + APPKIT_OKHTTP, + APPKIT_RETROFIT, DECRYPT_SIGN_MESSAGE, DECRYPT_AUTH_MESSAGE, DECRYPT_NOTIFY_MESSAGE, diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/AppKitModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/AppKitModule.kt new file mode 100644 index 000000000..6e444e15c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/AppKitModule.kt @@ -0,0 +1,66 @@ +package com.reown.android.internal.common.di + +import com.reown.android.BuildConfig +import com.reown.android.internal.common.modal.AppKitApiRepository +import com.reown.android.internal.common.modal.data.network.AppKitService +import com.reown.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCase +import com.reown.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCaseInterface +import com.reown.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCase +import com.reown.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface +import com.reown.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCase +import com.reown.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface +import com.reown.android.internal.common.modal.domain.usecase.GetWalletsUseCase +import com.reown.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface +import com.reown.android.internal.common.model.ProjectId +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory + +@JvmSynthetic +internal fun appKitModule() = module { + single(named(AndroidCommonDITags.WEB3MODAL_URL)) { "https://api.web3modal.com/" } + + single(named(AndroidCommonDITags.APPKIT_INTERCEPTOR)) { + Interceptor { chain -> + val updatedRequest = chain.request().newBuilder() + .addHeader("x-project-id", get().value) + .addHeader("x-sdk-version", "kotlin-${BuildConfig.SDK_VERSION}") + .build() + chain.proceed(updatedRequest) + } + } + + single(named(AndroidCommonDITags.APPKIT_OKHTTP)) { + get(named(AndroidCommonDITags.OK_HTTP)) + .newBuilder() + .addInterceptor(get(named(AndroidCommonDITags.APPKIT_INTERCEPTOR))) + .build() + } + + single(named(AndroidCommonDITags.APPKIT_RETROFIT)) { + Retrofit.Builder() + .baseUrl(get(named(AndroidCommonDITags.WEB3MODAL_URL))) + .client(get(named(AndroidCommonDITags.APPKIT_OKHTTP))) + .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) + .build() + } + + single { get(named(AndroidCommonDITags.APPKIT_RETROFIT)).create(AppKitService::class.java) } + + single { + AppKitApiRepository( + web3ModalApiUrl = get(named(AndroidCommonDITags.WEB3MODAL_URL)), + appKitService = get(), + context = androidContext() + ) + } + + single { GetInstalledWalletsIdsUseCase(appKitApiRepository = get()) } + single { GetWalletsUseCase(appKitApiRepository = get()) } + single { GetSampleWalletsUseCase(context = get()) } + single { EnableAnalyticsUseCase(repository = get()) } +} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/BaseStorageModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/BaseStorageModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt index aefb1ee9a..a1a5e8178 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/BaseStorageModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt @@ -1,32 +1,32 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di import app.cash.sqldelight.ColumnAdapter import app.cash.sqldelight.EnumColumnAdapter import com.squareup.moshi.Moshi -import com.walletconnect.android.di.AndroidBuildVariantDITags -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.internal.common.storage.identity.IdentitiesStorageRepository -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepository -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.pairing.PairingStorageRepository -import com.walletconnect.android.internal.common.storage.pairing.PairingStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.sdk.core.AndroidCoreDatabase -import com.walletconnect.android.sdk.storage.data.dao.EventDao -import com.walletconnect.android.sdk.storage.data.dao.JsonRpcHistoryDao -import com.walletconnect.android.sdk.storage.data.dao.MetaData -import com.walletconnect.android.sdk.storage.data.dao.VerifyContext -import com.walletconnect.utils.Empty +import com.reown.android.di.AndroidBuildVariantDITags +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.internal.common.storage.identity.IdentitiesStorageRepository +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepository +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.pairing.PairingStorageRepository +import com.reown.android.internal.common.storage.pairing.PairingStorageRepositoryInterface +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.sdk.core.AndroidCoreDatabase +import com.reown.android.sdk.storage.data.dao.EventDao +import com.reown.android.sdk.storage.data.dao.JsonRpcHistoryDao +import com.reown.android.sdk.storage.data.dao.MetaData +import com.reown.android.sdk.storage.data.dao.VerifyContext +import com.reown.utils.Empty import kotlinx.coroutines.launch import org.koin.core.qualifier.named import org.koin.core.scope.Scope import org.koin.dsl.module -import com.walletconnect.android.internal.common.scope as wcScope +import com.reown.android.internal.common.scope as wcScope fun baseStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { single, String>>(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCommonModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCommonModule.kt new file mode 100644 index 000000000..40b131652 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCommonModule.kt @@ -0,0 +1,69 @@ +package com.reown.android.internal.common.di + +import com.squareup.moshi.Moshi +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory +import com.tinder.scarlet.utils.getRawType +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.adapter.ExpiryAdapter +import com.reown.android.internal.common.adapter.JsonRpcResultAdapter +import com.reown.android.internal.common.adapter.TagsAdapter +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Tags +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.di.FoundationDITags +import com.reown.foundation.di.foundationCommonModule +import com.reown.foundation.util.Logger +import org.koin.core.qualifier.named +import org.koin.dsl.module +import timber.log.Timber +import kotlin.reflect.jvm.jvmName + +fun coreCommonModule() = module { + + includes(foundationCommonModule()) + + single> { + PolymorphicJsonAdapterFactory.of(JsonRpcResponse::class.java, "type") + .withSubtype(JsonRpcResponse.JsonRpcResult::class.java, "result") + .withSubtype(JsonRpcResponse.JsonRpcError::class.java, "error") + } + + single(named(AndroidCommonDITags.MOSHI)) { + get(named(FoundationDITags.MOSHI)) + .newBuilder() + .add { type, _, moshi -> + when (type.getRawType().name) { + Expiry::class.jvmName -> ExpiryAdapter + Tags::class.jvmName -> TagsAdapter + JsonRpcResponse.JsonRpcResult::class.jvmName -> JsonRpcResultAdapter(moshi) + else -> null + } + } + .add(get>()) + .add(get>()) + } + + single { + Timber + } + + single(named(AndroidCommonDITags.LOGGER)) { + object : Logger { + override fun log(logMsg: String?) { + get().d(logMsg) + } + + override fun log(throwable: Throwable?) { + get().d(throwable) + } + + override fun error(errorMsg: String?) { + get().e(errorMsg) + } + + override fun error(throwable: Throwable?) { + get().e(throwable) + } + } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCryptoModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCryptoModule.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCryptoModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCryptoModule.kt index 26dc9649d..0de91abc7 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCryptoModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreCryptoModule.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di import android.content.Context import android.content.SharedPreferences @@ -7,21 +7,21 @@ import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey -import com.walletconnect.android.internal.common.crypto.codec.ChaChaPolyCodec -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.crypto.kmr.BouncyCastleKeyManagementRepository -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.clientid.ClientIdJwtRepositoryAndroid -import com.walletconnect.android.internal.common.storage.key_chain.KeyChain -import com.walletconnect.foundation.crypto.data.repository.ClientIdJwtRepository -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.crypto.codec.ChaChaPolyCodec +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.crypto.kmr.BouncyCastleKeyManagementRepository +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.jwt.clientid.ClientIdJwtRepositoryAndroid +import com.reown.android.internal.common.storage.key_chain.KeyChain +import com.reown.foundation.crypto.data.repository.ClientIdJwtRepository +import com.reown.foundation.util.Logger import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.core.scope.Scope import org.koin.dsl.module import java.io.File import java.security.KeyStore -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore as WCKeyStore +import com.reown.android.internal.common.storage.key_chain.KeyStore as WCKeyStore private const val ANDROID_KEY_STORE = "AndroidKeyStore" private const val SHARED_PREFS_FILE = "wc_key_store" diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreJsonRpcModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreJsonRpcModule.kt new file mode 100644 index 000000000..429295146 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreJsonRpcModule.kt @@ -0,0 +1,55 @@ +package com.reown.android.internal.common.di + +import com.squareup.moshi.Moshi +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractor +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.json_rpc.domain.relay.RelayJsonRpcInteractor +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.model.type.SerializableJsonRpc +import com.reown.android.pairing.model.PairingJsonRpcMethod +import com.reown.android.pairing.model.PairingRpc +import com.reown.utils.* +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module +import kotlin.reflect.KClass + + +@JvmSynthetic +fun coreJsonRpcModule() = module { + + single { + RelayJsonRpcInteractor( + relay = get(), + chaChaPolyCodec = get(), + jsonRpcHistory = get(), + pushMessageStorage = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + backoffStrategy = get() + ) + } + + addSerializerEntry(PairingRpc.PairingPing::class) + addSerializerEntry(PairingRpc.PairingDelete::class) + + addDeserializerEntry(PairingJsonRpcMethod.WC_PAIRING_PING, PairingRpc.PairingPing::class) + addDeserializerEntry(PairingJsonRpcMethod.WC_PAIRING_DELETE, PairingRpc.PairingDelete::class) + + factory { + JsonRpcSerializer( + serializerEntries = getAll>().toSet(), + deserializerEntries = getAll>>().toMap(), + jsonAdapterEntries = getAll>().toSet(), + moshiBuilder = get(named(AndroidCommonDITags.MOSHI)) + ) + } + + single { + LinkModeJsonRpcInteractor( + chaChaPolyCodec = get(), + jsonRpcHistory = get(), + context = androidContext() + ) + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreNetworkModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreNetworkModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt index ba56b0fda..52e3ce157 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreNetworkModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di import android.net.Uri import android.os.Build @@ -6,18 +6,18 @@ import com.pandulapeter.beagle.logOkHttp.BeagleOkHttpLogger import com.squareup.moshi.Moshi import com.tinder.scarlet.Lifecycle import com.tinder.scarlet.Scarlet -import com.tinder.scarlet.lifecycle.android.AndroidLifecycle import com.tinder.scarlet.messageadapter.moshi.MoshiMessageAdapter -import com.tinder.scarlet.retry.ExponentialBackoffStrategy import com.tinder.scarlet.websocket.okhttp.newWebSocketFactory -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.internal.common.connection.ConnectivityState -import com.walletconnect.android.internal.common.connection.ManualConnectionLifecycle -import com.walletconnect.android.internal.common.jwt.clientid.GenerateJwtStoreClientIdUseCase -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.NetworkClientTimeout -import com.walletconnect.foundation.network.data.adapter.FlowStreamAdapter -import com.walletconnect.foundation.network.data.service.RelayService +import com.reown.android.BuildConfig +import com.reown.android.internal.common.ConditionalExponentialBackoffStrategy +import com.reown.android.internal.common.connection.ConnectivityState +import com.reown.android.internal.common.connection.DefaultConnectionLifecycle +import com.reown.android.internal.common.connection.ManualConnectionLifecycle +import com.reown.android.internal.common.jwt.clientid.GenerateJwtStoreClientIdUseCase +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.NetworkClientTimeout +import com.reown.foundation.network.data.adapter.FlowStreamAdapter +import com.reown.foundation.network.data.service.RelayService import okhttp3.Authenticator import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -47,7 +47,7 @@ fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, } factory(named(AndroidCommonDITags.USER_AGENT)) { - """wc-2/kotlin-${sdkVersion}/android-${Build.VERSION.RELEASE}""" + """wc-2/reown-kotlin-${sdkVersion}/android-${Build.VERSION.RELEASE}""" } single { @@ -109,17 +109,17 @@ fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, ManualConnectionLifecycle() } - single(named(AndroidCommonDITags.AUTOMATIC_CONNECTION_LIFECYCLE)) { - AndroidLifecycle.ofApplicationForeground(androidApplication()) + single(named(AndroidCommonDITags.DEFAULT_CONNECTION_LIFECYCLE)) { + DefaultConnectionLifecycle(androidApplication()) } - single { ExponentialBackoffStrategy(INIT_BACKOFF_MILLIS, TimeUnit.SECONDS.toMillis(MAX_BACKOFF_SEC)) } + single { ConditionalExponentialBackoffStrategy(INIT_BACKOFF_MILLIS, TimeUnit.SECONDS.toMillis(MAX_BACKOFF_SEC)) } single { FlowStreamAdapter.Factory() } single(named(AndroidCommonDITags.SCARLET)) { Scarlet.Builder() - .backoffStrategy(get()) + .backoffStrategy((get())) .webSocketFactory(get(named(AndroidCommonDITags.OK_HTTP)).newWebSocketFactory(get(named(AndroidCommonDITags.RELAY_URL)))) .lifecycle(getLifecycle(connectionType)) .addMessageAdapterFactory(get(named(AndroidCommonDITags.MSG_ADAPTER))) @@ -136,9 +136,9 @@ fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, } } -private fun Scope.getLifecycle(connectionType: ConnectionType) = +private fun Scope.getLifecycle(connectionType: ConnectionType): Lifecycle = if (connectionType == ConnectionType.MANUAL) { get(named(AndroidCommonDITags.MANUAL_CONNECTION_LIFECYCLE)) } else { - get(named(AndroidCommonDITags.AUTOMATIC_CONNECTION_LIFECYCLE)) + get(named(AndroidCommonDITags.DEFAULT_CONNECTION_LIFECYCLE)) } \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/CorePairingModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CorePairingModule.kt new file mode 100644 index 000000000..5253baa46 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CorePairingModule.kt @@ -0,0 +1,27 @@ +package com.reown.android.internal.common.di + +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.engine.domain.PairingEngine +import com.reown.android.pairing.handler.PairingControllerInterface +import org.koin.core.qualifier.named +import org.koin.dsl.module + +fun corePairingModule(pairing: PairingInterface, pairingController: PairingControllerInterface) = module { + single { + PairingEngine( + selfMetaData = get(), + crypto = get(), + metadataRepository = get(), + pairingRepository = get(), + jsonRpcInteractor = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + insertTelemetryEventUseCase = get(), + insertEventUseCase = get(), + sendBatchEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + userAgent = get(named(AndroidCommonDITags.USER_AGENT)) + ) + } + single { pairing } + single { pairingController } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/DatabaseConfig.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/DatabaseConfig.kt similarity index 77% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/DatabaseConfig.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/DatabaseConfig.kt index d1071537d..4bc474c3a 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/DatabaseConfig.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/DatabaseConfig.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di -import com.walletconnect.utils.Empty +import com.reown.utils.Empty import org.koin.android.ext.koin.androidContext import org.koin.core.scope.Scope @@ -12,13 +12,10 @@ class DatabaseConfig(private val storagePrefix: String = String.Empty) { val SIGN_SDK_DB_NAME get() = storagePrefix + "WalletConnectV2.db" - val CHAT_SDK_DB_NAME - get() = storagePrefix + "WalletConnectV2_chat.db" - val NOTIFY_SDK_DB_NAME get() = storagePrefix + "WalletConnectV2_notify.db" - val dbNames: List = listOf(ANDROID_CORE_DB_NAME, SIGN_SDK_DB_NAME, CHAT_SDK_DB_NAME, NOTIFY_SDK_DB_NAME) + val dbNames: List = listOf(ANDROID_CORE_DB_NAME, SIGN_SDK_DB_NAME, NOTIFY_SDK_DB_NAME) } fun Scope.deleteDatabase(dbName: String) { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/ExplorerModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/ExplorerModule.kt new file mode 100644 index 000000000..07444932e --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/ExplorerModule.kt @@ -0,0 +1,38 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.di + +import com.reown.android.internal.common.explorer.ExplorerRepository +import com.reown.android.internal.common.explorer.data.network.ExplorerService +import com.reown.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase +import com.reown.android.internal.common.explorer.domain.usecase.GetProjectsWithPaginationUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory + +@JvmSynthetic +internal fun explorerModule() = module { + + single(named(AndroidCommonDITags.EXPLORER_URL)) { "https://registry.walletconnect.org/" } + + single(named(AndroidCommonDITags.EXPLORER_RETROFIT)) { + Retrofit.Builder() + .baseUrl(get(named(AndroidCommonDITags.EXPLORER_URL))) + .client(get(named(AndroidCommonDITags.OK_HTTP))) + .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) + .build() + } + + single { get(named(AndroidCommonDITags.EXPLORER_RETROFIT)).create(ExplorerService::class.java) } + + single { + ExplorerRepository( + explorerService = get(), + projectId = get(), + ) + } + + single { GetProjectsWithPaginationUseCase(get()) } + single { GetNotifyConfigUseCase(get()) } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/KeyServerModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/KeyServerModule.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/KeyServerModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/KeyServerModule.kt index 5203eeb55..0beeff51a 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/KeyServerModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/KeyServerModule.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.android.keyserver.domain.use_case.* +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.android.keyserver.domain.use_case.* import org.koin.core.qualifier.named import org.koin.dsl.module import retrofit2.Retrofit diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/PulseModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/PulseModule.kt new file mode 100644 index 000000000..33f9d9226 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/PulseModule.kt @@ -0,0 +1,62 @@ +package com.reown.android.internal.common.di + +import com.squareup.moshi.Moshi +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.pulse.data.PulseService +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.domain.SendBatchEventUseCase +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.domain.SendEventUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory + +@JvmSynthetic +fun pulseModule(bundleId: String) = module { + single(named(AndroidCommonDITags.PULSE_URL)) { "https://pulse.walletconnect.org" } + + single(named(AndroidCommonDITags.PULSE_RETROFIT)) { + Retrofit.Builder() + .baseUrl(get(named(AndroidCommonDITags.PULSE_URL))) + .client(get(named(AndroidCommonDITags.APPKIT_OKHTTP))) + .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)).build())) + .build() + } + + single { + get(named(AndroidCommonDITags.PULSE_RETROFIT)).create(PulseService::class.java) + } + + single { + SendEventUseCase( + pulseService = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + bundleId = bundleId + ) + } + + single { + SendBatchEventUseCase( + pulseService = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + telemetryEnabled = get(named(AndroidCommonDITags.TELEMETRY_ENABLED)), + eventsRepository = get(), + ) + } + + single { + InsertTelemetryEventUseCase( + logger = get(named(AndroidCommonDITags.LOGGER)), + eventsRepository = get(), + ) + } + + single { + InsertEventUseCase( + logger = get(named(AndroidCommonDITags.LOGGER)), + eventsRepository = get(), + ) + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PushModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/PushModule.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PushModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/PushModule.kt index a63776bb4..1a1c5f182 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PushModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/PushModule.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di -import com.walletconnect.android.push.network.PushService -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.android.push.network.PushService +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface import org.koin.core.qualifier.named import org.koin.dsl.module import retrofit2.Retrofit diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/VerifyModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/VerifyModule.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/VerifyModule.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/di/VerifyModule.kt index 991feea5c..04c55cffd 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/VerifyModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/VerifyModule.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.internal.common.di +package com.reown.android.internal.common.di -import com.walletconnect.android.verify.data.VerifyService -import com.walletconnect.android.verify.domain.JWTRepository -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.android.verify.domain.VerifyPublicKeyStorageRepository -import com.walletconnect.android.verify.domain.VerifyRepository +import com.reown.android.verify.data.VerifyService +import com.reown.android.verify.domain.JWTRepository +import com.reown.android.verify.domain.ResolveAttestationIdUseCase +import com.reown.android.verify.domain.VerifyPublicKeyStorageRepository +import com.reown.android.verify.domain.VerifyRepository import org.koin.core.qualifier.named import org.koin.dsl.module import retrofit2.Retrofit diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/CommonWalletConnectExceptions.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/CommonWalletConnectExceptions.kt similarity index 96% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/CommonWalletConnectExceptions.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/exception/CommonWalletConnectExceptions.kt index 01f41dea6..3ecff093e 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/CommonWalletConnectExceptions.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/CommonWalletConnectExceptions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.exception +package com.reown.android.internal.common.exception class GenericException(override val message: String?) : WalletConnectException(message) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/exception/Messages.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/Messages.kt new file mode 100644 index 000000000..5be30ace6 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/Messages.kt @@ -0,0 +1,4 @@ +package com.reown.android.internal.common.exception + +const val WRONG_CONNECTION_TYPE: String = "Wrong connection type. Please, choose manual connection on initialisation." +internal const val DISCONNECT_MESSAGE: String = "User disconnected" \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/exception/PeerError.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/PeerError.kt new file mode 100644 index 000000000..caa61f289 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/PeerError.kt @@ -0,0 +1,37 @@ +package com.reown.android.internal.common.exception + +import com.reown.android.internal.common.model.type.Error + +sealed class Uncategorized : Error { + + data class NoMatchingTopic(val sequence: String, val topic: String) : Uncategorized() { + override val message: String = "No matching $sequence with topic: $topic" + override val code: Int = 1301 + } + + data class GenericError(val error: String) : Uncategorized() { + override val message: String = "Generic error: $error" + override val code: Int = 1302 + } +} + +sealed class Invalid : Error { + + data class MethodUnsupported(val method: String) : Invalid() { + override val message: String = "Unsupported Method Requested: $method" + override val code: Int = 10001 + } + + object RequestExpired: Invalid() { + override val message: String = "Request expired" + override val code: Int = 8000 + } +} + +sealed class Reason : Error { + + object UserDisconnected : Reason() { + override val message: String = DISCONNECT_MESSAGE + override val code: Int = 6000 + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/WalletConnectException.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/WalletConnectException.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/WalletConnectException.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/exception/WalletConnectException.kt index 3a37c940a..cee9991a3 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/WalletConnectException.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/exception/WalletConnectException.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.internal.common.exception +package com.reown.android.internal.common.exception -import com.walletconnect.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.AccountId abstract class WalletConnectException(override val message: String?) : Exception(message) internal class UnableToExtractDomainException(keyserverUrl: String) : WalletConnectException("Unable to extract domain from: $keyserverUrl") diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerInterface.kt new file mode 100644 index 000000000..12faa9593 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerInterface.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.explorer + +import com.reown.android.internal.common.explorer.data.model.Project + +interface ExplorerInterface { + suspend fun getProjects(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerProtocol.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerProtocol.kt new file mode 100644 index 000000000..e60cceff5 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerProtocol.kt @@ -0,0 +1,22 @@ +package com.reown.android.internal.common.explorer + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.explorer.data.model.Project +import com.reown.android.internal.common.explorer.domain.usecase.GetProjectsWithPaginationUseCase +import com.reown.android.internal.common.wcKoinApp +import com.reown.foundation.util.Logger +import org.koin.core.KoinApplication +import org.koin.core.qualifier.named + + +//discuss: Opening more endpoints to SDK consumers +class ExplorerProtocol( + private val koinApp: KoinApplication = wcKoinApp, +) : ExplorerInterface { + private val getProjectsWithPaginationUseCase: GetProjectsWithPaginationUseCase by lazy { koinApp.koin.get() } + private val logger: Logger by lazy { koinApp.koin.get(named(AndroidCommonDITags.LOGGER)) } + + override suspend fun getProjects(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> = getProjectsWithPaginationUseCase(page, entries, isVerified, isFeatured) +} + + diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerRepository.kt new file mode 100644 index 000000000..1cca6fb08 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/ExplorerRepository.kt @@ -0,0 +1,175 @@ +package com.reown.android.internal.common.explorer + +import androidx.core.net.toUri +import com.reown.android.internal.common.explorer.data.model.App +import com.reown.android.internal.common.explorer.data.model.Colors +import com.reown.android.internal.common.explorer.data.model.DappListings +import com.reown.android.internal.common.explorer.data.model.Desktop +import com.reown.android.internal.common.explorer.data.model.ImageUrl +import com.reown.android.internal.common.explorer.data.model.Injected +import com.reown.android.internal.common.explorer.data.model.Listing +import com.reown.android.internal.common.explorer.data.model.Metadata +import com.reown.android.internal.common.explorer.data.model.Mobile +import com.reown.android.internal.common.explorer.data.model.NotificationType +import com.reown.android.internal.common.explorer.data.model.NotifyConfig +import com.reown.android.internal.common.explorer.data.model.Project +import com.reown.android.internal.common.explorer.data.model.ProjectListing +import com.reown.android.internal.common.explorer.data.model.SupportedStandard +import com.reown.android.internal.common.explorer.data.network.ExplorerService +import com.reown.android.internal.common.explorer.data.network.model.AppDTO +import com.reown.android.internal.common.explorer.data.network.model.ColorsDTO +import com.reown.android.internal.common.explorer.data.network.model.DappListingsDTO +import com.reown.android.internal.common.explorer.data.network.model.DesktopDTO +import com.reown.android.internal.common.explorer.data.network.model.ImageUrlDTO +import com.reown.android.internal.common.explorer.data.network.model.InjectedDTO +import com.reown.android.internal.common.explorer.data.network.model.ListingDTO +import com.reown.android.internal.common.explorer.data.network.model.MetadataDTO +import com.reown.android.internal.common.explorer.data.network.model.MobileDTO +import com.reown.android.internal.common.explorer.data.network.model.NotificationTypeDTO +import com.reown.android.internal.common.explorer.data.network.model.NotifyConfigDTO +import com.reown.android.internal.common.explorer.data.network.model.ProjectDTO +import com.reown.android.internal.common.explorer.data.network.model.ProjectListingDTO +import com.reown.android.internal.common.explorer.data.network.model.SupportedStandardDTO +import com.reown.android.internal.common.model.ProjectId + +//discuss: Repository could be inside domain +class ExplorerRepository( + private val explorerService: ExplorerService, + private val projectId: ProjectId, +) { + + suspend fun getAllDapps(): DappListings { + return with(explorerService.getAllDapps(projectId.value)) { + if (isSuccessful && body() != null) { + body()!!.toDappListing() + } else { + throw Throwable(errorBody()?.string()) + } + } + } + + suspend fun getProjects( + page: Int, + entries: Int, + isVerified: Boolean, + isFeatured: Boolean, + ): ProjectListing { + return with(explorerService.getProjects(projectId.value, entries, page, isVerified, isFeatured)) { + if (isSuccessful && body() != null) { + body()!!.toProjectListing() + } else { + throw Throwable(errorBody()?.string()) + } + } + } + + suspend fun getNotifyConfig( + appDomain: String, + ): NotifyConfig { + return with(explorerService.getNotifyConfig(projectId.value, appDomain)) { + if (isSuccessful && body() != null) { + body()!!.toNotifyConfig() + } else { + throw Throwable(errorBody()?.string()) + } + } + } + + private fun NotifyConfigDTO.toNotifyConfig(): NotifyConfig { + return with(data) { + NotifyConfig( + types = notificationTypes.map { it.toNotificationType() }, + name = name, + description = description, + imageUrl = imageUrl?.toImageUrl(), + homepage = homepage ?: "", + dappUrl = dappUrl, + isVerified = isVerified, + ) + } + } + + private fun NotificationTypeDTO.toNotificationType(): NotificationType = NotificationType(name = name, id = id, description = description, imageUrl = imageUrl?.toImageUrl()) + + + private fun ProjectListingDTO.toProjectListing(): ProjectListing { + return ProjectListing( + projects = projects.values.map { it.toProject() }, + count = count, + ) + } + + private fun ProjectDTO.toProject(): Project = Project( + id = id, + name = name?.takeIf { it.isNotBlank() } ?: "Name not provided", + description = description?.takeIf { it.isNotBlank() } ?: "Description not provided", + homepage = homepage?.takeIf { it.isNotBlank() } ?: "Homepage not provided", + imageId = imageId?.takeIf { it.isNotBlank() } ?: "ImageID not provided", + imageUrl = imageUrl?.toImageUrl() ?: ImageUrl("", "", ""), + dappUrl = dappUrl?.takeIf { it.isNotBlank() } ?: "Dapp url not provided", + order = order, + ) + + private fun DappListingsDTO.toDappListing(): DappListings { + return DappListings( + listings = listings.values.map { it.toListing() }, count = count, total = total + ) + } + + private fun ListingDTO.toListing(): Listing = Listing( + id = id, + name = name, + description = description, + homepage = homepage.toUri(), + chains = chains, + versions = versions, + sdks = sdks, + appType = appType, + imageId = imageId, + imageUrl = imageUrl.toImageUrl(), + app = app.toApp(), + injected = injected?.map { it.toInjected() }, + mobile = mobile.toMobile(), + desktop = desktop.toDesktop(), + supportedStandards = supportedStandards.map { it.toSupportedStandard() }, + metadata = metadata.toMetadata(), + updatedAt = updatedAt + ) + + private fun ImageUrlDTO.toImageUrl(): ImageUrl = ImageUrl( + sm = sm, + md = md, + lg = lg, + ) + + private fun AppDTO.toApp(): App = App( + browser = browser, ios = ios, android = android, mac = mac, windows = windows, linux = linux, chrome = chrome, firefox = firefox, safari = safari, edge = edge, opera = opera + ) + + private fun InjectedDTO.toInjected(): Injected = Injected( + namespace = namespace, injectedId = injectedId + ) + + private fun MobileDTO.toMobile(): Mobile = Mobile( + native = native, + universal = universal, + ) + + private fun DesktopDTO.toDesktop(): Desktop = Desktop( + native = native, + universal = universal, + ) + + private fun SupportedStandardDTO.toSupportedStandard(): SupportedStandard = SupportedStandard( + id = id, url = url, title = title, standardId = standardId, standardPrefix = standardPrefix + ) + + private fun MetadataDTO.toMetadata(): Metadata = Metadata( + shortName = shortName, + colors = colors.toColors(), + ) + + private fun ColorsDTO.toColors(): Colors = Colors( + primary = primary, secondary = secondary + ) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/App.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/App.kt similarity index 80% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/App.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/App.kt index 461ba1201..88bf45fa2 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/App.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/App.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.model +package com.reown.android.internal.common.explorer.data.model data class App( val browser: String?, diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Colors.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Colors.kt new file mode 100644 index 000000000..53cce9073 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Colors.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class Colors( + val primary: String?, + val secondary: String? +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/DappListings.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/DappListings.kt new file mode 100644 index 000000000..34ea38a8e --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/DappListings.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.explorer.data.model + +data class DappListings( + val listings: List, + val count: Int, + val total: Int +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Desktop.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Desktop.kt new file mode 100644 index 000000000..8186d86de --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Desktop.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class Desktop( + val native: String, + val universal: String? +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ImageUrl.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ImageUrl.kt new file mode 100644 index 000000000..a67b02d31 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ImageUrl.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.explorer.data.model + +data class ImageUrl( + val sm: String, + val md: String, + val lg: String +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Injected.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Injected.kt new file mode 100644 index 000000000..ef126b8dc --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Injected.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class Injected( + val namespace: String, + val injectedId: String +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Listing.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Listing.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Listing.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Listing.kt index 9d80e57e7..247c38fe0 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Listing.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Listing.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.model +package com.reown.android.internal.common.explorer.data.model import android.net.Uri diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Metadata.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Metadata.kt new file mode 100644 index 000000000..bb945363a --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Metadata.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class Metadata( + val shortName: String?, + val colors: Colors +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Mobile.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Mobile.kt new file mode 100644 index 000000000..07d04ba18 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Mobile.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class Mobile( + val native: String?, + val universal: String? +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/NotifyConfig.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/NotifyConfig.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/NotifyConfig.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/NotifyConfig.kt index 4a390d9bf..50edc3161 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/NotifyConfig.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/NotifyConfig.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.explorer.data.model +package com.reown.android.internal.common.explorer.data.model data class NotifyConfig( val dappUrl: String, diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Project.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Project.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Project.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Project.kt index b0517f171..e9878d853 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Project.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/Project.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.model +package com.reown.android.internal.common.explorer.data.model data class Project( val id: String, diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ProjectListing.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ProjectListing.kt new file mode 100644 index 000000000..890f284fc --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/ProjectListing.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.explorer.data.model + +data class ProjectListing( + val projects: List, + val count: Int, +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/SupportedStandard.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/SupportedStandard.kt new file mode 100644 index 000000000..78948ce33 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/model/SupportedStandard.kt @@ -0,0 +1,9 @@ +package com.reown.android.internal.common.explorer.data.model + +data class SupportedStandard( + val id: String, + val url: String, + val title: String, + val standardId: Int, + val standardPrefix: String +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/ExplorerService.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/ExplorerService.kt new file mode 100644 index 000000000..1b546356f --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/ExplorerService.kt @@ -0,0 +1,42 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.explorer.data.network + +import com.reown.android.internal.common.explorer.data.network.model.DappListingsDTO +import com.reown.android.internal.common.explorer.data.network.model.NotifyConfigDTO +import com.reown.android.internal.common.explorer.data.network.model.ProjectListingDTO +import com.reown.android.internal.common.explorer.data.network.model.WalletListingDTO +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Query + +interface ExplorerService { + + @GET("v3/dapps") + suspend fun getAllDapps(@Query("projectId") projectId: String): Response + + @GET("w3i/v1/projects") + suspend fun getProjects( + @Query("projectId") projectId: String, + @Query("entries") entries: Int, + @Query("page") page: Int, + @Query("isVerified") isVerified: Boolean, + @Query("isFeatured") isFeatured: Boolean, + ): Response + + @GET("w3i/v1/notify-config") + suspend fun getNotifyConfig( + @Query("projectId") projectId: String, + @Query("appDomain") appDomain: String, + ): Response + + @GET("w3m/v1/getAndroidListings") + suspend fun getAndroidWallets( + @Query("projectId") projectId: String, + @Query("chains") chains: String?, + @Query("sdkType") sdkType: String, + @Query("sdkVersion") sdkVersion: String, + @Query("excludedIds") excludedIds: String?, + @Query("recommendedIds") recommendedIds: String?, + ): Response +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/AppDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/AppDTO.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/AppDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/AppDTO.kt index bb7d549cb..0b4845bf1 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/AppDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/AppDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ColorsDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ColorsDTO.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ColorsDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ColorsDTO.kt index b098cbfc8..f1977e55a 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ColorsDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ColorsDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTO.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTO.kt index a3ae31dc7..589ec6b39 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DesktopDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DesktopDTO.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DesktopDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DesktopDTO.kt index b8b7cc6aa..0556d4cc5 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DesktopDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/DesktopDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt index 17db64bed..85561a759 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ImageUrlDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/InjectedDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/InjectedDTO.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/InjectedDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/InjectedDTO.kt index b6f79704b..240f90d38 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/InjectedDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/InjectedDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ListingDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ListingDTO.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ListingDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ListingDTO.kt index 9f713c528..fef2517b4 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ListingDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ListingDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MetadataDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MetadataDTO.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MetadataDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MetadataDTO.kt index 0b11262da..b3460080d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MetadataDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MetadataDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MobileDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MobileDTO.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MobileDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MobileDTO.kt index d73e1cf0b..6ca4c4b6b 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/MobileDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/MobileDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt similarity index 92% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt index 52c2fa2a8..34e519eee 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/NotifyConfigDTO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectDTO.kt similarity index 87% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectDTO.kt index 85f5c6b13..1cc3e1c80 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt index 8675e8a44..c44329289 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/ProjectListingDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt similarity index 83% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt index a51053380..d0ef7dc39 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/SupportedStandardDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletDTO.kt new file mode 100644 index 000000000..9ea0171a3 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletDTO.kt @@ -0,0 +1,22 @@ +package com.reown.android.internal.common.explorer.data.network.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class WalletDTO( + @Json(name = "id") + val id: String, + @Json(name = "name") + val name: String, + @Json(name = "description") + val description: String?, + @Json(name = "homepage") + val homePage: String, + @Json(name = "image_id") + val imageId: String, + @Json(name = "mobile") + val mobile: MobileDTO, + @Json(name = "app") + val app: AppDTO, +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletListingDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletListingDTO.kt new file mode 100644 index 000000000..4f1881815 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/data/network/model/WalletListingDTO.kt @@ -0,0 +1,12 @@ +package com.reown.android.internal.common.explorer.data.network.model + +import com.squareup.moshi.Json + +data class WalletListingDTO( + @Json(name = "listings") + val listings: Map, + @Json(name = "count") + val count: Int, + @Json(name = "total") + val total: Int +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt new file mode 100644 index 000000000..afd9a3045 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt @@ -0,0 +1,8 @@ +package com.reown.android.internal.common.explorer.domain.usecase + +import com.reown.android.internal.common.explorer.ExplorerRepository +import com.reown.android.internal.common.explorer.data.model.NotifyConfig + +class GetNotifyConfigUseCase(private val explorerRepository: ExplorerRepository) { + suspend operator fun invoke(appDomain: String): Result = runCatching { explorerRepository.getNotifyConfig(appDomain) } +} diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt new file mode 100644 index 000000000..a1ec861fd --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt @@ -0,0 +1,11 @@ +package com.reown.android.internal.common.explorer.domain.usecase + +import com.reown.android.internal.common.explorer.ExplorerRepository +import com.reown.android.internal.common.explorer.data.model.Project + +class GetProjectsWithPaginationUseCase( + private val explorerRepository: ExplorerRepository, +) { + suspend operator fun invoke(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> = + runCatching { explorerRepository.getProjects(page, entries, isVerified, isFeatured).projects.sortedBy { it.order ?: Long.MAX_VALUE } } +} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/data/JsonRpcSerializer.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/data/JsonRpcSerializer.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/data/JsonRpcSerializer.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/data/JsonRpcSerializer.kt index 2ad32f283..61ece3831 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/data/JsonRpcSerializer.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/data/JsonRpcSerializer.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.internal.common.json_rpc.data +package com.reown.android.internal.common.json_rpc.data import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.android.internal.common.model.type.SerializableJsonRpc -import com.walletconnect.utils.JsonAdapterEntry +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.android.internal.common.model.type.SerializableJsonRpc +import com.reown.utils.JsonAdapterEntry import kotlin.reflect.KClass import kotlin.reflect.safeCast diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt index 9918553b2..2eb313429 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/link_mode/LinkModeJsonRpcInteractor.kt @@ -1,28 +1,28 @@ -package com.walletconnect.android.internal.common.json_rpc.domain.link_mode +package com.reown.android.internal.common.json_rpc.domain.link_mode import android.content.Context import android.content.Intent import android.net.Uri import android.util.Base64 -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.toWCResponse -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.android.internal.common.model.type.JsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.util.Empty +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.toWCResponse +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.android.internal.common.model.type.JsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.wcKoinApp +import com.reown.foundation.common.model.Topic +import com.reown.util.Empty import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt index 8210cdeac..9e15724e8 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/domain/relay/RelayJsonRpcInteractor.kt @@ -1,40 +1,41 @@ -package com.walletconnect.android.internal.common.json_rpc.domain.relay - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.android.internal.common.exception.NoInternetConnectionException -import com.walletconnect.android.internal.common.exception.NoRelayConnectionException -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.toRelay -import com.walletconnect.android.internal.common.json_rpc.model.toWCRequest -import com.walletconnect.android.internal.common.json_rpc.model.toWCResponse -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.model.type.Error -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.relay.RelayConnectionInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.common.model.SubscriptionId -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.foundation.util.Logger -import com.walletconnect.utils.Empty +package com.reown.android.internal.common.json_rpc.domain.relay + +import com.reown.android.internal.common.ConditionalExponentialBackoffStrategy +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.android.internal.common.exception.NoInternetConnectionException +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.toRelay +import com.reown.android.internal.common.json_rpc.model.toWCRequest +import com.reown.android.internal.common.json_rpc.model.toWCResponse +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.Error +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.internal.utils.ObservableMap +import com.reown.android.relay.RelayConnectionInterface +import com.reown.android.relay.WSSConnectionState +import com.reown.foundation.common.model.SubscriptionId +import com.reown.foundation.common.model.Topic +import com.reown.foundation.network.model.Relay +import com.reown.foundation.util.Logger +import com.reown.utils.Empty +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -61,6 +62,7 @@ internal class RelayJsonRpcInteractor( private val jsonRpcHistory: JsonRpcHistory, private val pushMessageStorage: PushMessagesRepository, private val logger: Logger, + private val backoffStrategy: ConditionalExponentialBackoffStrategy ) : RelayJsonRpcInteractorInterface { private val serializer: JsonRpcSerializer get() = wcKoinApp.koin.get() @@ -74,28 +76,17 @@ internal class RelayJsonRpcInteractor( override val internalErrors: SharedFlow = _internalErrors.asSharedFlow() override val wssConnectionState: StateFlow get() = relay.wssConnectionState - private val subscriptions: MutableMap = mutableMapOf() + private var subscriptions = ObservableMap { newMap -> if (newMap.isEmpty()) backoffStrategy.shouldBackoff(false) } + override val onResubscribe: Flow get() = relay.onResubscribe init { manageSubscriptions() } - override fun checkConnectionWorking() { + override fun checkNetworkConnectivity() { if (relay.isNetworkAvailable.value != null && relay.isNetworkAvailable.value == false) { throw NoInternetConnectionException("Connection error: Please check your Internet connection") } - - if (relay.wssConnectionState.value is WSSConnectionState.Disconnected) { - val message = when (relay.wssConnectionState.value) { - is WSSConnectionState.Disconnected.ConnectionClosed -> - (relay.wssConnectionState.value as WSSConnectionState.Disconnected.ConnectionClosed).message ?: "WSS connection closed" - - is WSSConnectionState.Disconnected.ConnectionFailed -> (relay.wssConnectionState.value as WSSConnectionState.Disconnected.ConnectionFailed).throwable.message - - else -> "WSS connection closed" - } - throw NoRelayConnectionException("Connection error: No Relay connection: $message") - } } override fun publishJsonRpcRequest( @@ -108,18 +99,13 @@ internal class RelayJsonRpcInteractor( onFailure: (Throwable) -> Unit, ) { try { - checkConnectionWorking() + checkNetworkConnectivity() } catch (e: NoConnectivityException) { return onFailure(e) } - val requestJson = try { - serializer.serialize(payload) ?: return onFailure(IllegalStateException("JsonRpcInteractor: Unknown result params")) - } catch (e: Exception) { - return onFailure(e) - } - try { + val requestJson = serializer.serialize(payload) ?: throw IllegalStateException("RelayJsonRpcInteractor: Unknown Request Params") if (jsonRpcHistory.setRequest(payload.id, topic, payload.method, requestJson, TransportType.RELAY)) { val encryptedRequest = chaChaPolyCodec.encrypt(topic, requestJson, envelopeType, participants) val encryptedRequestString = Base64.toBase64String(encryptedRequest) @@ -136,7 +122,7 @@ internal class RelayJsonRpcInteractor( } } catch (e: Exception) { logger.error("JsonRpcInteractor: Cannot send the request, exception: $e") - return onFailure(e) + onFailure(Throwable("Publish Request Error: $e")) } } @@ -150,16 +136,15 @@ internal class RelayJsonRpcInteractor( envelopeType: EnvelopeType, ) { try { - checkConnectionWorking() + checkNetworkConnectivity() } catch (e: NoConnectivityException) { return onFailure(e) } try { - val responseJson = serializer.serialize(response) ?: return onFailure(IllegalStateException("JsonRpcInteractor: Unknown result params")) + val responseJson = serializer.serialize(response) ?: throw IllegalStateException("RelayJsonRpcInteractor: Unknown Response Params") val encryptedResponse = chaChaPolyCodec.encrypt(topic, responseJson, envelopeType, participants) val encryptedResponseString = Base64.toBase64String(encryptedResponse) - relay.publish(topic.value, encryptedResponseString, params.toRelay()) { result -> result.fold( onSuccess = { @@ -174,8 +159,94 @@ internal class RelayJsonRpcInteractor( } } catch (e: Exception) { logger.error("JsonRpcInteractor: Cannot send the response, exception: $e") + onFailure(Throwable("Publish Response Error: $e")) + } + } + + override fun subscribe(topic: Topic, onSuccess: (Topic) -> Unit, onFailure: (Throwable) -> Unit) { + try { + checkNetworkConnectivity() + } catch (e: NoConnectivityException) { return onFailure(e) } + + try { + backoffStrategy.shouldBackoff(true) + relay.subscribe(topic.value) { result -> + result.fold( + onSuccess = { acknowledgement -> + subscriptions[topic.value] = acknowledgement.result + onSuccess(topic) + }, + onFailure = { error -> + logger.error("Subscribe to topic error: $topic error: $error") + onFailure(Throwable("Subscribe error: ${error.message}")) + } + ) + } + } catch (e: Exception) { + logger.error("Subscribe to topic error: $topic error: $e") + onFailure(Throwable("Subscribe error: ${e.message}")) + } + } + + override fun batchSubscribe(topics: List, onSuccess: (List) -> Unit, onFailure: (Throwable) -> Unit) { + try { + checkNetworkConnectivity() + } catch (e: NoConnectivityException) { + return onFailure(e) + } + + if (topics.isNotEmpty()) { + backoffStrategy.shouldBackoff(true) + try { + relay.batchSubscribe(topics) { result -> + result.fold( + onSuccess = { acknowledgement -> + subscriptions.plusAssign(topics.zip(acknowledgement.result).toMap()) + onSuccess(topics) + }, + onFailure = { error -> + logger.error("Batch subscribe to topics error: $topics error: $error") + onFailure(Throwable("Batch subscribe error: ${error.message}")) + } + ) + } + } catch (e: Exception) { + logger.error("Batch subscribe to topics error: $topics error: $e") + onFailure(Throwable("Batch subscribe error: ${e.message}")) + } + } + } + + override fun unsubscribe(topic: Topic, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) { + try { + checkNetworkConnectivity() + } catch (e: NoConnectivityException) { + return onFailure(e) + } + + if (subscriptions.contains(topic.value)) { + val subscriptionId = SubscriptionId(subscriptions[topic.value].toString()) + relay.unsubscribe(topic.value, subscriptionId.id) { result -> + result.fold( + onSuccess = { + scope.launch { + supervisorScope { + jsonRpcHistory.deleteRecordsByTopic(topic) + subscriptions.remove(topic.value) + pushMessageStorage.deletePushMessagesByTopic(topic.value) + onSuccess() + } + } + }, + onFailure = { error -> + logger.error("Unsubscribe to topic: $topic error: $error") + onFailure(Throwable("Unsubscribe error: ${error.message}")) + } + ) + } + } } override fun respondWithParams( @@ -280,82 +351,6 @@ internal class RelayJsonRpcInteractor( } } - override fun subscribe(topic: Topic, onSuccess: (Topic) -> Unit, onFailure: (Throwable) -> Unit) { - try { - checkConnectionWorking() - } catch (e: NoConnectivityException) { - return onFailure(e) - } - - relay.subscribe(topic.value) { result -> - result.fold( - onSuccess = { acknowledgement -> - subscriptions[topic.value] = acknowledgement.result - onSuccess(topic) - }, - onFailure = { error -> - logger.error("Subscribe to topic error: $topic error: $error") - onFailure(Throwable("Subscribe error: ${error.message}")) - } - ) - } - } - - override fun batchSubscribe(topics: List, onSuccess: (List) -> Unit, onFailure: (Throwable) -> Unit) { - try { - checkConnectionWorking() - } catch (e: NoConnectivityException) { - return onFailure(e) - } - - if (topics.isNotEmpty()) { - relay.batchSubscribe(topics) { result -> - result.fold( - onSuccess = { acknowledgement -> - subscriptions.plusAssign(topics.zip(acknowledgement.result).toMap()) - onSuccess(topics) - }, - onFailure = { error -> - logger.error("Batch subscribe to topics error: $topics error: $error") - onFailure(Throwable("Batch subscribe error: ${error.message}")) - } - ) - } - } - } - - override fun unsubscribe(topic: Topic, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) { - try { - checkConnectionWorking() - } catch (e: NoConnectivityException) { - return onFailure(e) - } - - if (subscriptions.contains(topic.value)) { - val subscriptionId = SubscriptionId(subscriptions[topic.value].toString()) - relay.unsubscribe(topic.value, subscriptionId.id) { result -> - result.fold( - onSuccess = { - scope.launch { - supervisorScope { - jsonRpcHistory.deleteRecordsByTopic(topic) - subscriptions.remove(topic.value) - pushMessageStorage.deletePushMessagesByTopic(topic.value) - onSuccess() - } - } - }, - onFailure = { error -> - logger.error("Unsubscribe to topic: $topic error: $error") - onFailure(Throwable("Unsubscribe error: ${error.message}")) - } - ) - } - } else { - onFailure(NoSuchElementException(Uncategorized.NoMatchingTopic("Session", topic.value).message)) - } - } - private fun manageSubscriptions() { scope.launch { relay.subscriptionRequest.map { relayRequest -> diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt new file mode 100644 index 000000000..1a126f245 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt @@ -0,0 +1,12 @@ +package com.reown.android.internal.common.json_rpc.model + +import com.reown.android.internal.common.model.TransportType + +data class JsonRpcHistoryRecord( + val id: Long, + val topic: String, + val method: String, + val body: String, + val response: String?, + val transportType: TransportType? +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcMapper.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcMapper.kt new file mode 100644 index 000000000..c73e2a539 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/json_rpc/model/JsonRpcMapper.kt @@ -0,0 +1,25 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.json_rpc.model + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.json_rpc.domain.relay.Subscription +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.foundation.common.model.Topic +import com.reown.foundation.network.model.Relay + +@JvmSynthetic +internal fun JsonRpcHistoryRecord.toWCResponse(result: JsonRpcResponse, params: ClientParams): WCResponse = + WCResponse(Topic(topic), method, result, params) + +@JvmSynthetic +internal fun IrnParams.toRelay(): Relay.Model.IrnParams = + Relay.Model.IrnParams(tag.id, ttl.seconds, prompt) + +internal fun Subscription.toWCRequest(clientJsonRpc: ClientJsonRpc, params: ClientParams, transportType: TransportType): WCRequest = + WCRequest(topic, clientJsonRpc.id, clientJsonRpc.method, params, decryptedMessage, publishedAt, encryptedMessage, attestation, transportType) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt new file mode 100644 index 000000000..cfdd31d10 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt @@ -0,0 +1,30 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.jwt.clientid + +import com.reown.android.internal.common.exception.CannotFindKeyPairException +import com.reown.android.internal.common.storage.key_chain.KeyStore +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.crypto.data.repository.BaseClientIdJwtRepository + +internal class ClientIdJwtRepositoryAndroid(private val keyChain: KeyStore) : BaseClientIdJwtRepository() { + + override fun setKeyPair(key: String, privateKey: PrivateKey, publicKey: PublicKey) { + keyChain.setKeys(CLIENT_ID_KEYPAIR_TAG, privateKey, publicKey) + } + + override fun getKeyPair(): Pair { + return if (doesKeyPairExist()) { + val (privateKey, publicKey) = keyChain.getKeys(CLIENT_ID_KEYPAIR_TAG) + ?: throw CannotFindKeyPairException("No key pair for given tag: $CLIENT_ID_KEYPAIR_TAG") + publicKey to privateKey + } else { + generateAndStoreClientIdKeyPair() + } + } + + private fun doesKeyPairExist(): Boolean { + return keyChain.checkKeys(CLIENT_ID_KEYPAIR_TAG) + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt new file mode 100644 index 000000000..5d847d844 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt @@ -0,0 +1,19 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.jwt.clientid + +import android.content.SharedPreferences +import androidx.core.content.edit +import com.reown.android.internal.common.di.KEY_CLIENT_ID +import com.reown.android.utils.strippedUrl +import com.reown.foundation.crypto.data.repository.ClientIdJwtRepository + +internal class GenerateJwtStoreClientIdUseCase(private val clientIdJwtRepository: ClientIdJwtRepository, private val sharedPreferences: SharedPreferences) { + + operator fun invoke(relayUrl: String): String = + clientIdJwtRepository.generateJWT(relayUrl.strippedUrl()) { clientId -> + sharedPreferences.edit { + putString(KEY_CLIENT_ID, clientId) + } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/DidJwtRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/DidJwtRepository.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/DidJwtRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/DidJwtRepository.kt index 817ebd73f..22a8203b2 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/DidJwtRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/DidJwtRepository.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic @file:JvmName("DidJwtRepository") -package com.walletconnect.android.internal.common.jwt.did +package com.reown.android.internal.common.jwt.did -import com.walletconnect.android.internal.common.model.DidJwt -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.util.jwt.* +import com.reown.android.internal.common.model.DidJwt +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.util.jwt.* fun encodeDidJwt( identityPrivateKey: PrivateKey, diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt new file mode 100644 index 000000000..743947bb5 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt @@ -0,0 +1,28 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.jwt.did + +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.JwtClaims +import com.reown.foundation.util.jwt.encodeEd25519DidKey +import com.reown.foundation.util.jwt.jwtIatAndExp +import java.util.concurrent.TimeUnit + +interface EncodeDidJwtPayloadUseCase { + + operator fun invoke(params: Params): R + + data class Params(val identityPublicKey: PublicKey, val keyserverUrl: String, val expirySourceDuration: Long = 30, val expiryTimeUnit: TimeUnit = TimeUnit.DAYS) { + private val iatAndExp = jwtIatAndExp(timeunit = TimeUnit.SECONDS, expirySourceDuration = expirySourceDuration, expiryTimeUnit = expiryTimeUnit) + + val issuedAt: Long + get() = iatAndExp.first + + val expiration: Long + get() = iatAndExp.second + + val issuer: String + get() = encodeEd25519DidKey(identityPublicKey.keyAsBytes) + } +} + diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt similarity index 80% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt index 062beeba9..6362459fd 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/jwt/did/EncodeIdentityKeyDidJwtPayloadUseCase.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.internal.common.jwt.did +package com.reown.android.internal.common.jwt.did import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.util.jwt.JwtClaims -import com.walletconnect.foundation.util.jwt.encodeDidPkh +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.util.jwt.JwtClaims +import com.reown.foundation.util.jwt.encodeDidPkh internal class EncodeIdentityKeyDidJwtPayloadUseCase( private val accountId: AccountId, diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/AppKitApiRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/AppKitApiRepository.kt new file mode 100644 index 000000000..c1516246c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/AppKitApiRepository.kt @@ -0,0 +1,76 @@ +package com.reown.android.internal.common.modal + +import android.content.Context +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.internal.common.modal.data.model.WalletAppData +import com.reown.android.internal.common.modal.data.model.WalletListing +import com.reown.android.internal.common.modal.data.network.AppKitService +import com.reown.android.internal.common.modal.data.network.model.WalletDTO +import com.reown.android.internal.common.modal.data.network.model.WalletDataDTO +import com.reown.android.utils.isWalletInstalled + +internal class AppKitApiRepository( + private val context: Context, + private val web3ModalApiUrl: String, + private val appKitService: AppKitService +) { + + suspend fun getAndroidWalletsData(sdkType: String) = runCatching { + appKitService.getAndroidData(sdkType = sdkType) + }.mapCatching { response -> + response.body()!!.data.toWalletsAppData().filter { it.isInstalled } + } + + suspend fun getAnalyticsConfig(sdkType: String = "w3m") = runCatching { + appKitService.getAnalyticsConfig(sdkType = sdkType) + }.mapCatching { response -> + response.body()!!.isAnalyticsEnabled + } + + suspend fun getWallets( + sdkType: String, + page: Int, + search: String? = null, + excludeIds: List? = null, + includeWallets: List? = null + ) = runCatching { + appKitService.getWallets( + sdkType = sdkType, + page = page, + search = search, + exclude = excludeIds?.joinToString(","), + include = includeWallets?.joinToString(",") + ) + }.mapCatching { response -> + val body = response.body()!! + WalletListing( + page = page, + totalCount = body.count, + wallets = body.data.toWallets() + ) + } + + private fun List.toWallets(): List = map { walletDTO -> + Wallet( + id = walletDTO.id, + name = walletDTO.name, + homePage = walletDTO.homePage, + imageUrl = web3ModalApiUrl + "getWalletImage/${walletDTO.imageId}", + order = walletDTO.order, + mobileLink = walletDTO.mobileLink, + playStore = walletDTO.playStore, + webAppLink = walletDTO.webappLink, + linkMode = walletDTO.linkMode + ).apply { + isWalletInstalled = context.packageManager.isWalletInstalled(appPackage) + } + } + + private fun List.toWalletsAppData() = map { data -> + WalletAppData( + id = data.id, + appPackage = data.appId, + isInstalled = context.packageManager.isWalletInstalled(data.appId) + ) + } +} diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/Wallet.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/Wallet.kt new file mode 100644 index 000000000..22e7a330d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/Wallet.kt @@ -0,0 +1,28 @@ +package com.reown.android.internal.common.modal.data.model + +import android.net.Uri + +data class Wallet( + val id: String, + val name: String, + val homePage: String, + val imageUrl: String, + val order: String, + val mobileLink: String?, + val playStore: String?, + val webAppLink: String?, + val linkMode: String?, + val isRecommended: Boolean = false +) { + val appPackage: String? = playStore?.extractPackage() + var isRecent: Boolean = false + var isWalletInstalled: Boolean = false + + val hasMobileWallet: Boolean + get() = mobileLink != null + + val hasWebApp: Boolean + get() = webAppLink != null +} + +private fun String.extractPackage(): String? = Uri.parse(this).getQueryParameter("id") diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletAppData.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletAppData.kt new file mode 100644 index 000000000..f5e01394d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletAppData.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.modal.data.model + +data class WalletAppData( + val id: String, + val appPackage: String?, + val isInstalled: Boolean +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletListing.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletListing.kt new file mode 100644 index 000000000..92ae096a5 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/model/WalletListing.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.modal.data.model + +data class WalletListing( + val page: Int, + val totalCount: Int, + val wallets: List +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/AppKitService.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/AppKitService.kt new file mode 100644 index 000000000..7ec558311 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/AppKitService.kt @@ -0,0 +1,32 @@ +package com.reown.android.internal.common.modal.data.network + +import com.reown.android.internal.common.modal.data.network.model.EnableAnalyticsDTO +import com.reown.android.internal.common.modal.data.network.model.GetAndroidDataDTO +import com.reown.android.internal.common.modal.data.network.model.GetWalletsDTO +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Query + +internal interface AppKitService { + @GET("getWallets") + suspend fun getWallets( + @Header("x-sdk-type") sdkType: String, + @Query("page") page: Int, + @Query("search") search: String? = null, + @Query("exclude") exclude: String? = null, + @Query("include") include: String? = null, + @Query("entries") entries: Int = 100, + @Query("platform") platform: String = "android" + ): Response + + @GET("getAndroidData") + suspend fun getAndroidData( + @Header("x-sdk-type") sdkType: String, + ): Response + + @GET("getAnalyticsConfig") + suspend fun getAnalyticsConfig( + @Header("x-sdk-type") sdkType: String, + ): Response +} diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt new file mode 100644 index 000000000..c84068d08 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt @@ -0,0 +1,8 @@ +package com.reown.android.internal.common.modal.data.network.model + +import com.squareup.moshi.Json + +data class EnableAnalyticsDTO( + @Json(name = "isAnalyticsEnabled") + val isAnalyticsEnabled: Boolean +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt new file mode 100644 index 000000000..8b835064b --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal.common.modal.data.network.model + +import com.squareup.moshi.Json + +internal class GetAndroidDataDTO( + @Json(name = "count") + val count: Int, + @Json(name = "data") + val data: List, +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetWalletsDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetWalletsDTO.kt new file mode 100644 index 000000000..3c049c985 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/GetWalletsDTO.kt @@ -0,0 +1,11 @@ +package com.reown.android.internal.common.modal.data.network.model + +import com.squareup.moshi.Json +import com.reown.android.internal.common.modal.data.network.model.WalletDTO + +internal data class GetWalletsDTO( + @Json(name = "count") + val count: Int, + @Json(name = "data") + val data: List, +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDTO.kt new file mode 100644 index 000000000..93f216ea2 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDTO.kt @@ -0,0 +1,30 @@ +package com.reown.android.internal.common.modal.data.network.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class WalletDTO( + @Json(name = "id") + val id: String, + @Json(name = "name") + val name: String, + @Json(name = "homepage") + val homePage: String, + @Json(name = "image_id") + val imageId: String, + @Json(name = "order") + val order: String, + @Json(name = "mobile_link") + val mobileLink: String?, + @Json(name = "desktop_link") + val desktopLink: String?, + @Json(name = "webapp_link") + val webappLink: String?, + @Json(name = "app_store") + val appStore: String?, + @Json(name = "play_store") + val playStore: String?, + @Json(name = "link_mode") + val linkMode: String?, +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDataDTO.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDataDTO.kt new file mode 100644 index 000000000..b13cabdda --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/data/network/model/WalletDataDTO.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal.common.modal.data.network.model + +import com.squareup.moshi.Json + +class WalletDataDTO( + @Json(name = "id") + val id: String, + @Json(name = "android_app_id") + val appId: String?, +) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt new file mode 100644 index 000000000..15708a053 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt @@ -0,0 +1,25 @@ +package com.reown.android.internal.common.modal.domain.usecase + +import com.reown.android.internal.common.modal.AppKitApiRepository +import kotlinx.coroutines.runBlocking + +interface EnableAnalyticsUseCaseInterface { + fun fetchAnalyticsConfig(): Boolean +} + +internal class EnableAnalyticsUseCase(private val repository: AppKitApiRepository) : EnableAnalyticsUseCaseInterface { + override fun fetchAnalyticsConfig(): Boolean { + return runBlocking { + try { + val response = repository.getAnalyticsConfig() + if (response.isSuccess) { + response.getOrDefault(false) + } else { + false + } + } catch (e: Exception) { + false + } + } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt new file mode 100644 index 000000000..2e53d0bc2 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt @@ -0,0 +1,15 @@ +package com.reown.android.internal.common.modal.domain.usecase + +import com.reown.android.internal.common.modal.AppKitApiRepository + +interface GetInstalledWalletsIdsUseCaseInterface { + suspend operator fun invoke( + sdkType: String + ): List +} + +internal class GetInstalledWalletsIdsUseCase( + private val appKitApiRepository: AppKitApiRepository +) : GetInstalledWalletsIdsUseCaseInterface { + override suspend fun invoke(sdkType: String): List = appKitApiRepository.getAndroidWalletsData(sdkType).map { it.map { walletAppData -> walletAppData.id } }.getOrThrow() +} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt index 9682efef9..41cd42757 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetSamplesWalletsUseCaseInterface.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal.common.modal.domain.usecase +package com.reown.android.internal.common.modal.domain.usecase import android.content.Context -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.utils.isWalletInstalled +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.utils.isWalletInstalled interface GetSampleWalletsUseCaseInterface { suspend operator fun invoke(): List @@ -13,9 +13,9 @@ internal class GetSampleWalletsUseCase( ) : GetSampleWalletsUseCaseInterface { override suspend fun invoke(): List { val samples = mapOf( - "com.walletconnect.sample.wallet.debug" to SampleWalletDebug, - "com.walletconnect.sample.wallet.internal" to SampleWalletInternal, - "com.walletconnect.sample.wallet" to SampleWalletRelease, + "com.reown.sample.wallet.debug" to SampleWalletDebug, + "com.reown.sample.wallet.internal" to SampleWalletInternal, + "com.reown.sample.wallet" to SampleWalletRelease, "com.walletconnect.web3wallet.rnsample.internal" to RNSampleWallet, "com.walletconnect.flutterwallet" to FLSampleWallet, "com.walletconnect.flutterwallet.internal" to FLSampleWalletInternal @@ -39,7 +39,7 @@ private val SampleWalletDebug = Wallet( mobileLink = "kotlin-web3wallet://", playStore = null, webAppLink = null, - linkMode = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_debug", + linkMode = "https://dev.lab.web3modal.com/wallet_debug", true ) @@ -52,7 +52,7 @@ private val SampleWalletInternal = Wallet( mobileLink = "kotlin-web3wallet://", playStore = null, webAppLink = null, - linkMode = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_internal", + linkMode = "https://dev.lab.web3modal.com/wallet_internal", true ) @@ -65,7 +65,7 @@ private val SampleWalletRelease = Wallet( mobileLink = "kotlin-web3wallet://", playStore = null, webAppLink = null, - linkMode = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_release", + linkMode = "https://dev.lab.web3modal.com/wallet_release", true ) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt new file mode 100644 index 000000000..dceb160ac --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt @@ -0,0 +1,26 @@ +package com.reown.android.internal.common.modal.domain.usecase + +import com.reown.android.internal.common.modal.AppKitApiRepository +import com.reown.android.internal.common.modal.data.model.WalletListing + +interface GetWalletsUseCaseInterface { + suspend operator fun invoke( + sdkType: String, + page: Int, + search: String? = null, + excludeIds: List? = null, + includes: List? = null + ): WalletListing +} + +internal class GetWalletsUseCase( + private val appKitApiRepository: AppKitApiRepository +) : GetWalletsUseCaseInterface { + override suspend fun invoke( + sdkType: String, + page: Int, + search: String?, + excludeIds: List?, + includes: List? + ): WalletListing = appKitApiRepository.getWallets(sdkType, page, search, excludeIds, includes).getOrThrow() +} diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/AccountId.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AccountId.kt new file mode 100644 index 000000000..a0fa4a5d8 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AccountId.kt @@ -0,0 +1,12 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.model + +import com.reown.android.internal.utils.CoreValidator + + +@JvmInline +value class AccountId(val value: String) { + fun isValid(): Boolean = CoreValidator.isAccountIdCAIP10Compliant(value) + fun address() = value.split(":").last() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaData.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaData.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaData.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaData.kt index 77725cd81..73edf3cc1 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaData.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaData.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaDataType.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaDataType.kt new file mode 100644 index 000000000..2ac5a593e --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/AppMetaDataType.kt @@ -0,0 +1,5 @@ +package com.reown.android.internal.common.model + +enum class AppMetaDataType { + SELF, PEER +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/ConnectionState.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/ConnectionState.kt new file mode 100644 index 000000000..ca360d602 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/ConnectionState.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +data class ConnectionState(val isAvailable: Boolean, val reason: Reason? = null) : EngineEvent { + sealed class Reason { + data class ConnectionClosed(val message: String) : Reason() + data class ConnectionFailed(val throwable: Throwable) : Reason() + } +} diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/CryptoException.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/CryptoException.kt new file mode 100644 index 000000000..cace4f3f3 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/CryptoException.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.model + +import com.reown.android.internal.common.exception.WalletConnectException + +class UnknownEnvelopeTypeException(override val message: String?) : WalletConnectException(message) +class MissingParticipantsException(override val message: String?) : WalletConnectException(message) +class MissingKeyException(override val message: String?) : WalletConnectException(message) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/DidJwt.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/DidJwt.kt new file mode 100644 index 000000000..c27605fb6 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/DidJwt.kt @@ -0,0 +1,4 @@ +package com.reown.android.internal.common.model + +@JvmInline +value class DidJwt(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/EnvelopeType.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/EnvelopeType.kt new file mode 100644 index 000000000..e7fb281b9 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/EnvelopeType.kt @@ -0,0 +1,5 @@ +package com.reown.android.internal.common.model + +enum class EnvelopeType(val id: Byte) { + ZERO(0), ONE(1), TWO(2) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/Expiry.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Expiry.kt new file mode 100644 index 000000000..12f893e89 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Expiry.kt @@ -0,0 +1,3 @@ +package com.reown.android.internal.common.model + +data class Expiry(val seconds: Long) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/IrnParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/IrnParams.kt new file mode 100644 index 000000000..a06bb6a41 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/IrnParams.kt @@ -0,0 +1,5 @@ +package com.reown.android.internal.common.model + +import com.reown.foundation.common.model.Ttl + +data class IrnParams(val tag: Tags, val ttl: Ttl, val prompt: Boolean = false) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Namespace.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Namespace.kt similarity index 94% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Namespace.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/Namespace.kt index b9be227a0..cd2f4a263 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Namespace.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Namespace.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/PackageName.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/PackageName.kt new file mode 100644 index 000000000..5cb0bbfaa --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/PackageName.kt @@ -0,0 +1,4 @@ +package com.reown.android.internal.common.model + +@JvmInline +value class PackageName(val value: String) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Pairing.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Pairing.kt similarity index 80% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Pairing.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/Pairing.kt index b4aca8537..3dae354c1 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Pairing.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Pairing.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model -import com.walletconnect.android.internal.common.model.type.Sequence -import com.walletconnect.android.pairing.model.pairingExpiry -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.model.type.Sequence +import com.reown.android.pairing.model.pairingExpiry +import com.reown.foundation.common.model.Topic data class Pairing( override val topic: Topic, diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participant.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Participant.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participant.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/Participant.kt index 07948c160..251346545 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participant.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Participant.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/Participants.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Participants.kt new file mode 100644 index 000000000..115d0fa7a --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Participants.kt @@ -0,0 +1,8 @@ +package com.reown.android.internal.common.model + +import com.reown.foundation.common.model.PublicKey + +data class Participants( + val senderPublicKey: PublicKey, + val receiverPublicKey: PublicKey, +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/ProjectId.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/ProjectId.kt new file mode 100644 index 000000000..47a3f1f09 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/ProjectId.kt @@ -0,0 +1,3 @@ +package com.reown.android.internal.common.model + +data class ProjectId(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Redirect.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Redirect.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Redirect.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/Redirect.kt index 0be1bbc2c..3267a52ef 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Redirect.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Redirect.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/RelayProtocolOptions.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/RelayProtocolOptions.kt similarity index 77% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/RelayProtocolOptions.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/RelayProtocolOptions.kt index fe51849f8..eede7e5f4 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/RelayProtocolOptions.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/RelayProtocolOptions.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/SDKError.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SDKError.kt new file mode 100644 index 000000000..799c537f5 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SDKError.kt @@ -0,0 +1,5 @@ +package com.reown.android.internal.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +class SDKError(val exception: Throwable) : EngineEvent \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SessionProposer.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SessionProposer.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SessionProposer.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/SessionProposer.kt index 58968e639..41202afce 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SessionProposer.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SessionProposer.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/SymmetricKey.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SymmetricKey.kt new file mode 100644 index 000000000..dddb03542 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/SymmetricKey.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.model + +import com.reown.foundation.common.model.Key + +@JvmInline +value class SymmetricKey(override val keyAsHex: String) : Key \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Tags.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Tags.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Tags.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/Tags.kt index 95b6e1690..55c569737 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Tags.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Tags.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model enum class Tags(val id: Int) { UNSUPPORTED_METHOD(0), @@ -47,21 +47,6 @@ enum class Tags(val id: Int) { SESSION_REQUEST_LINK_MODE(1125), SESSION_REQUEST_LINK_MODE_RESPONSE(1126), - CHAT_INVITE(2000), - CHAT_INVITE_RESPONSE(2001), - - CHAT_MESSAGE(2002), - CHAT_MESSAGE_RESPONSE(2003), - - CHAT_LEAVE(2004), - CHAT_LEAVE_RESPONSE(2005), - - CHAT_PING(2006), - CHAT_PING_RESPONSE(2007), - - AUTH_REQUEST(3000), - AUTH_REQUEST_RESPONSE(3001), - NOTIFY_SUBSCRIBE(4000), NOTIFY_SUBSCRIBE_RESPONSE(4001), diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/TelemetryEnabled.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/TelemetryEnabled.kt new file mode 100644 index 000000000..ce108d5a7 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/TelemetryEnabled.kt @@ -0,0 +1,4 @@ +package com.reown.android.internal.common.model + +@JvmInline +value class TelemetryEnabled(val value: Boolean) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/TransportType.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/TransportType.kt new file mode 100644 index 000000000..a70451c1d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/TransportType.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.model + +enum class TransportType { + RELAY, + LINK_MODE +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/Validation.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Validation.kt new file mode 100644 index 000000000..1201fd63b --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/Validation.kt @@ -0,0 +1,7 @@ +package com.reown.android.internal.common.model + +enum class Validation { + VALID, + INVALID, + UNKNOWN +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCRequest.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCRequest.kt new file mode 100644 index 000000000..c59a5b25e --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCRequest.kt @@ -0,0 +1,17 @@ +package com.reown.android.internal.common.model + +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty + +data class WCRequest( + val topic: Topic, + val id: Long, + val method: String, + val params: ClientParams, + val message: String = String.Empty, + val publishedAt: Long = 0, + val encryptedMessage: String = String.Empty, + val attestation: String? = null, + val transportType: TransportType +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCResponse.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCResponse.kt new file mode 100644 index 000000000..34aa97096 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WCResponse.kt @@ -0,0 +1,12 @@ +package com.reown.android.internal.common.model + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.foundation.common.model.Topic + +data class WCResponse( + val topic: Topic, + val method: String, + val response: JsonRpcResponse, + val params: ClientParams, +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WalletConnectUri.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WalletConnectUri.kt similarity index 87% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WalletConnectUri.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/WalletConnectUri.kt index 51dafd2db..53664a5d5 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WalletConnectUri.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/WalletConnectUri.kt @@ -1,8 +1,8 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.model +package com.reown.android.internal.common.model -import com.walletconnect.foundation.common.model.Topic +import com.reown.foundation.common.model.Topic data class WalletConnectUri( val topic: Topic, diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt similarity index 75% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt index f953c8171..0855010de 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/ChatNotifyResponseAuthParams.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal.common.model.params +package com.reown.android.internal.common.model.params import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.ClientParams interface ChatNotifyResponseAuthParams { @JsonClass(generateAdapter = true) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreChatParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreChatParams.kt new file mode 100644 index 000000000..5dd77eef3 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreChatParams.kt @@ -0,0 +1,14 @@ +package com.reown.android.internal.common.model.params + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.type.ClientParams + +sealed interface CoreChatParams : ClientParams { + + @JsonClass(generateAdapter = true) + data class ReceiptParams( + @Json(name = "receiptAuth") + val receiptAuth: String, + ) : CoreChatParams +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreNotifyParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreNotifyParams.kt similarity index 90% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreNotifyParams.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreNotifyParams.kt index 79d251b23..d398ba472 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreNotifyParams.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreNotifyParams.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.model.params +package com.reown.android.internal.common.model.params import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.ClientParams sealed interface CoreNotifyParams : ClientParams { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreSignParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreSignParams.kt new file mode 100644 index 000000000..541d0d26f --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/params/CoreSignParams.kt @@ -0,0 +1,30 @@ +package com.reown.android.internal.common.model.params + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.Participant +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.signing.cacao.Cacao + +open class CoreSignParams : ClientParams { + + @JsonClass(generateAdapter = true) + data class ApprovalParams( + @Json(name = "relay") + val relay: RelayProtocolOptions, + @Json(name = "responderPublicKey") + val responderPublicKey: String, + ) : CoreSignParams() + + @JsonClass(generateAdapter = true) + data class SessionAuthenticateApproveParams( + @Json(name = "responder") + val responder: Participant, + @Json(name = "cacaos") + val cacaos: List, + ) : CoreSignParams() { + val linkMode = responder.metadata.redirect?.linkMode + val appLink = responder.metadata.redirect?.universal + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/sync/ClientJsonRpc.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/sync/ClientJsonRpc.kt new file mode 100644 index 000000000..5104e2aca --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/sync/ClientJsonRpc.kt @@ -0,0 +1,10 @@ +package com.reown.android.internal.common.model.sync + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ClientJsonRpc( + val id: Long, + val jsonrpc: String, + val method: String +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/ClientParams.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/ClientParams.kt new file mode 100644 index 000000000..3b7caa934 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/ClientParams.kt @@ -0,0 +1,3 @@ +package com.reown.android.internal.common.model.type + +interface ClientParams : SerializableJsonRpc \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/EngineEvent.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/EngineEvent.kt new file mode 100644 index 000000000..5ac871e7d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/EngineEvent.kt @@ -0,0 +1,3 @@ +package com.reown.android.internal.common.model.type + +interface EngineEvent \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Error.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Error.kt new file mode 100644 index 000000000..440f171b8 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Error.kt @@ -0,0 +1,6 @@ +package com.reown.android.internal.common.model.type + +interface Error { + val message: String + val code: Int +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcClientSync.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcClientSync.kt new file mode 100644 index 000000000..5979a501c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcClientSync.kt @@ -0,0 +1,8 @@ +package com.reown.android.internal.common.model.type + +interface JsonRpcClientSync : SerializableJsonRpc { + val id: Long + val method: String + val jsonrpc: String + val params: T +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcInteractorInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcInteractorInterface.kt new file mode 100644 index 000000000..61f127050 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/JsonRpcInteractorInterface.kt @@ -0,0 +1,12 @@ +package com.reown.android.internal.common.model.type + +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.WCResponse +import kotlinx.coroutines.flow.SharedFlow + +interface JsonRpcInteractorInterface { + val clientSyncJsonRpc: SharedFlow + val peerResponse: SharedFlow + val internalErrors: SharedFlow +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt index 017090b2c..4d4c0ad6b 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/RelayJsonRpcInteractorInterface.kt @@ -1,17 +1,19 @@ -package com.walletconnect.android.internal.common.model.type +package com.reown.android.internal.common.model.type -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.relay.WSSConnectionState +import com.reown.foundation.common.model.Topic +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow interface RelayJsonRpcInteractorInterface : JsonRpcInteractorInterface { val wssConnectionState: StateFlow - fun checkConnectionWorking() + val onResubscribe: Flow + fun checkNetworkConnectivity() fun subscribe(topic: Topic, onSuccess: (Topic) -> Unit = {}, onFailure: (Throwable) -> Unit = {}) diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Sequence.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Sequence.kt new file mode 100644 index 000000000..7eb769a2d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/Sequence.kt @@ -0,0 +1,9 @@ +package com.reown.android.internal.common.model.type + +import com.reown.android.internal.common.model.Expiry +import com.reown.foundation.common.model.Topic + +interface Sequence { + val topic: Topic + val expiry: Expiry +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/SerializableJsonRpc.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/SerializableJsonRpc.kt new file mode 100644 index 000000000..8bbffb4c4 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/model/type/SerializableJsonRpc.kt @@ -0,0 +1,3 @@ +package com.reown.android.internal.common.model.type + +interface SerializableJsonRpc \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Cacao.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Cacao.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Cacao.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Cacao.kt index 6d0bd1069..b2cf5fa77 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Cacao.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Cacao.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.internal.common.signing.cacao +package com.reown.android.internal.common.signing.cacao import androidx.annotation.Keep import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX -import com.walletconnect.android.internal.common.signing.signature.Signature +import com.reown.android.cacao.SignatureInterface +import com.reown.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX +import com.reown.android.internal.common.signing.signature.Signature @JsonClass(generateAdapter = true) data class Cacao( diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoType.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoType.kt new file mode 100644 index 000000000..d9e72e64f --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoType.kt @@ -0,0 +1,8 @@ +package com.reown.android.internal.common.signing.cacao + +enum class CacaoType(val header: String) { + EIP4361("eip4361"), + CAIP222("caip222"); + + fun toHeader(): Cacao.Header = Cacao.Header(this.header) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoVerifier.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoVerifier.kt new file mode 100644 index 000000000..6b4778e7e --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/CacaoVerifier.kt @@ -0,0 +1,26 @@ +package com.reown.android.internal.common.signing.cacao + +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.signature.verify +import org.web3j.utils.Numeric + +class CacaoVerifier(private val projectId: ProjectId) { + fun verify(cacao: Cacao): Boolean = when (cacao.signature.t) { + + SignatureType.EIP191.header, SignatureType.EIP1271.header -> { + val plainMessage = cacao.payload.toCAIP222Message() + val hexMessage = Numeric.toHexString(cacao.payload.toCAIP222Message().toByteArray()) + val address = Issuer(cacao.payload.iss).address + + if (cacao.signature.toSignature().verify(plainMessage, address, cacao.signature.t, projectId)) { + true + } else { + cacao.signature.toSignature().verify(hexMessage, address, cacao.signature.t, projectId) + } + } + + else -> throw RuntimeException("Invalid header") + } +} + diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Issuer.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Issuer.kt similarity index 92% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Issuer.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Issuer.kt index 230fea701..e74a6abf1 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Issuer.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Issuer.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.signing.cacao +package com.reown.android.internal.common.signing.cacao data class Issuer(val value: String) { val chainId diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Utils.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Utils.kt new file mode 100644 index 000000000..80a6cceed --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/cacao/Utils.kt @@ -0,0 +1,115 @@ +package com.reown.android.internal.common.signing.cacao + +import android.util.Base64 +import com.reown.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX +import com.reown.utils.HexPrefix +import org.json.JSONArray +import org.json.JSONObject + +@JvmSynthetic +internal fun String.guaranteeNoHexPrefix(): String = removePrefix(String.HexPrefix) + +@JvmSynthetic +fun String?.parseReCaps(): MutableMap>> { + if (this.isNullOrEmpty()) return emptyMap>>().toMutableMap() + val reCapsMap: MutableMap>> = mutableMapOf() + + val jsonObject = JSONObject(this) + val attObject = jsonObject.getJSONObject("att") + + attObject.keys().forEach { key -> + val innerObject = attObject.getJSONObject(key) + val requestsMap = mutableMapOf>() + + innerObject.keys().forEach { requestType -> + val requestArray = innerObject.getJSONArray(requestType) + val dynamicList = mutableListOf() + + for (i in 0 until requestArray.length()) { + val itemObject = requestArray.getJSONObject(i) + // Assuming the structure under each requestType contains arrays of strings + itemObject.keys().forEach { dynamicKey -> + val dynamicArray = itemObject.getJSONArray(dynamicKey) + for (j in 0 until dynamicArray.length()) { + dynamicList.add(dynamicArray.getString(j)) + } + } + } + + requestsMap[requestType] = dynamicList + } + + reCapsMap[key] = requestsMap + } + + return reCapsMap.mapValues { entry -> entry.value.toMutableMap() }.toMutableMap() +} + +@JvmSynthetic +fun List?.decodeReCaps(): String? { + return try { + val last = this?.last() + if (last != null && last.startsWith(RECAPS_PREFIX)) { + Base64.decode(last.removePrefix(RECAPS_PREFIX), Base64.NO_WRAP).toString(Charsets.UTF_8) + } else { + null + } + } catch (e: Exception) { + null + } +} + +@JvmSynthetic +fun List?.getMethods(): List { + return this.decodeReCaps().parseReCaps()["eip155"]?.keys?.sorted()?.map { key -> key.substringAfter('/') } ?: emptyList() +} + +@JvmSynthetic +fun List?.getChains(): List { + return this.decodeReCaps().parseReCaps()["eip155"]?.values?.flatten()?.distinct() ?: emptyList() +} + +@JvmSynthetic +fun mergeReCaps(json1: JSONObject, json2: JSONObject): String { + val result = JSONObject(json1.toString()) // Start with a deep copy of json1 + + json2.keys().forEach { key -> + if (!result.has(key)) { + // If json1 does not have the key, simply put the json2 object/array/primitive + result.put(key, json2.get(key)) + } else { + // If both json1 and json2 have the object, merge them + val value1 = result.get(key) + val value2 = json2.get(key) + + when { + value1 is JSONObject && value2 is JSONObject -> { + result.put(key, mergeReCaps(value1, value2)) + } + + value1 is JSONArray && value2 is JSONArray -> { + // Concatenate arrays, respecting ordering rules if specified + val mergedArray = concatenateJsonArrays(value1, value2) + result.put(key, mergedArray) + } + + else -> { + // For primitive types or if types are different, json2 overrides json1 + result.put(key, value2) + } + } + } + } + return result.toString().replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}") +} + +private fun concatenateJsonArrays(arr1: JSONArray, arr2: JSONArray): JSONArray { + val result = JSONArray() + for (i in 0 until arr1.length()) { + result.put(arr1.get(i)) + } + for (i in 0 until arr2.length()) { + result.put(arr2.get(i)) + } + return result +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip1271/EIP1271Verifier.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip1271/EIP1271Verifier.kt similarity index 91% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip1271/EIP1271Verifier.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip1271/EIP1271Verifier.kt index 6341cf6c9..c58867926 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip1271/EIP1271Verifier.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip1271/EIP1271Verifier.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.internal.common.signing.eip1271 +package com.reown.android.internal.common.signing.eip1271 -import com.walletconnect.android.internal.common.signing.signature.Signature -import com.walletconnect.android.internal.common.signing.signature.toCacaoSignature -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.generateId +import com.reown.android.internal.common.signing.signature.Signature +import com.reown.android.internal.common.signing.signature.toCacaoSignature +import com.reown.util.bytesToHex +import com.reown.util.generateId import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Signer.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Signer.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Signer.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Signer.kt index a0ce9f60d..b0171634e 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Signer.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Signer.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal.common.signing.eip191 +package com.reown.android.internal.common.signing.eip191 -import com.walletconnect.android.internal.common.signing.model.HexString -import com.walletconnect.android.internal.common.signing.signature.Signature -import com.walletconnect.android.internal.common.signing.signature.toSignature +import com.reown.android.internal.common.signing.model.HexString +import com.reown.android.internal.common.signing.signature.Signature +import com.reown.android.internal.common.signing.signature.toSignature import org.web3j.crypto.ECKeyPair import org.web3j.crypto.Sign import org.web3j.utils.Numeric.hexStringToByteArray diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Verifier.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Verifier.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Verifier.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Verifier.kt index ab0e010df..3a1c5d02c 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/eip191/EIP191Verifier.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/eip191/EIP191Verifier.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal.common.signing.eip191 +package com.reown.android.internal.common.signing.eip191 -import com.walletconnect.android.internal.common.signing.cacao.guaranteeNoHexPrefix -import com.walletconnect.android.internal.common.signing.signature.Signature -import com.walletconnect.android.internal.common.signing.signature.toSignatureData +import com.reown.android.internal.common.signing.cacao.guaranteeNoHexPrefix +import com.reown.android.internal.common.signing.signature.Signature +import com.reown.android.internal.common.signing.signature.toSignatureData import org.web3j.crypto.Keys import org.web3j.crypto.Sign import org.web3j.utils.Numeric.hexStringToByteArray diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/signing/message/MessageSignatureVerifier.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/message/MessageSignatureVerifier.kt new file mode 100644 index 000000000..dbe91e17d --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/message/MessageSignatureVerifier.kt @@ -0,0 +1,12 @@ +package com.reown.android.internal.common.signing.message + +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.signature.Signature +import com.reown.android.internal.common.signing.signature.verify + + +class MessageSignatureVerifier(private val projectId: ProjectId) { + fun verify(signature: String, originalMessage: String, address: String, type: SignatureType): Boolean = + Signature.fromString(signature).verify(originalMessage, address, type.header, projectId) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/signing/model/HexString.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/model/HexString.kt new file mode 100644 index 000000000..97a51c631 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/model/HexString.kt @@ -0,0 +1,4 @@ +package com.reown.android.internal.common.signing.model + +@JvmInline +value class HexString(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/Signature.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/Signature.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/Signature.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/Signature.kt index b408b3dd8..fbf0a821e 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/Signature.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/Signature.kt @@ -1,15 +1,15 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.signing.signature +package com.reown.android.internal.common.signing.signature -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.cacao.guaranteeNoHexPrefix -import com.walletconnect.android.internal.common.signing.eip1271.EIP1271Verifier -import com.walletconnect.android.internal.common.signing.eip191.EIP191Verifier -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes -import com.walletconnect.utils.HexPrefix +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.cacao.guaranteeNoHexPrefix +import com.reown.android.internal.common.signing.eip1271.EIP1271Verifier +import com.reown.android.internal.common.signing.eip191.EIP191Verifier +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes +import com.reown.utils.HexPrefix import org.web3j.crypto.Sign @JvmSynthetic diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureInterface.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureInterface.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureInterface.kt index af19cdcc8..2170a1f24 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureInterface.kt @@ -1,6 +1,6 @@ @file:Suppress("PackageDirectoryMismatch") -package com.walletconnect.android.cacao +package com.reown.android.cacao interface SignatureInterface { val t: String diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureType.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureType.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureType.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureType.kt index f86ed1d1c..37fb685df 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/signature/SignatureType.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/signing/signature/SignatureType.kt @@ -1,5 +1,5 @@ @file:Suppress("PackageDirectoryMismatch") -package com.walletconnect.android.cacao.signature +package com.reown.android.cacao.signature // Note: Szymon - Only added to have backwards compatibility. This ties SignatureTypes from android-core to sdks specific implementations. When we decide to remove deprecated sdk specific diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/events/EventsRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/events/EventsRepository.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/events/EventsRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/events/EventsRepository.kt index 3463f721f..b48c9ffe8 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/events/EventsRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/events/EventsRepository.kt @@ -1,13 +1,13 @@ -package com.walletconnect.android.internal.common.storage.events +package com.reown.android.internal.common.storage.events import android.database.sqlite.SQLiteException import app.cash.sqldelight.async.coroutines.awaitAsList -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.pulse.model.Event -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.sdk.storage.data.dao.EventDao -import com.walletconnect.android.sdk.storage.data.dao.EventQueries +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.pulse.model.Event +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.sdk.storage.data.dao.EventDao +import com.reown.android.sdk.storage.data.dao.EventQueries import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -38,7 +38,8 @@ class EventsRepository( this.props.properties?.trace, this.props.properties?.correlationId, this.props.properties?.clientId, - this.props.properties?.direction + this.props.properties?.direction, + this.props.properties?.userAgent ) } } @@ -85,7 +86,8 @@ class EventsRepository( trace = trace, clientId = client_id, correlationId = correlation_id, - direction = direction + direction = direction, + userAgent = user_agent ) ) ) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/identity/IdentitiesStorageRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/identity/IdentitiesStorageRepository.kt similarity index 78% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/identity/IdentitiesStorageRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/identity/IdentitiesStorageRepository.kt index 27aa81e34..e0dde327d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/identity/IdentitiesStorageRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/identity/IdentitiesStorageRepository.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.internal.common.storage.identity +package com.reown.android.internal.common.storage.identity import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.sdk.storage.data.dao.IdentitiesQueries +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.sdk.storage.data.dao.IdentitiesQueries class IdentitiesStorageRepository(private val identities: IdentitiesQueries, moshiBuilder: Moshi.Builder) { private val moshi = moshiBuilder.build() diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyChain.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyChain.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyChain.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyChain.kt index ae1d2a0d8..71eefabef 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyChain.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyChain.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.storage.key_chain +package com.reown.android.internal.common.storage.key_chain import android.content.SharedPreferences -import com.walletconnect.foundation.common.model.Key -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes +import com.reown.foundation.common.model.Key +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes internal class KeyChain(private val sharedPreferences: SharedPreferences) : KeyStore { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyStore.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyStore.kt new file mode 100644 index 000000000..ddc56a9d7 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/key_chain/KeyStore.kt @@ -0,0 +1,16 @@ +@file:JvmSynthetic + +package com.reown.android.internal.common.storage.key_chain + +import com.reown.foundation.common.model.Key + +interface KeyStore { + fun getKey(tag: String): String? + fun setKey(tag: String, key: Key) + + fun getKeys(tag: String): Pair? + fun setKeys(tag: String, key1: Key, key2: Key) + + fun deleteKeys(tag: String) + fun checkKeys(tag: String): Boolean +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepository.kt similarity index 85% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepository.kt index 8c3d8e721..56b8de819 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepository.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.storage.metadata +package com.reown.android.internal.common.storage.metadata import android.database.sqlite.SQLiteException -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.android.sdk.storage.data.dao.MetaDataQueries -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Redirect +import com.reown.android.sdk.storage.data.dao.MetaDataQueries +import com.reown.foundation.common.model.Topic internal class MetadataStorageRepository(private val metaDataQueries: MetaDataQueries) : MetadataStorageRepositoryInterface { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt new file mode 100644 index 000000000..1c1ab8277 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt @@ -0,0 +1,22 @@ +package com.reown.android.internal.common.storage.metadata + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.foundation.common.model.Topic + +interface MetadataStorageRepositoryInterface { + + fun insertOrAbortMetadata(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) + + fun updateMetaData(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) + + suspend fun updateOrAbortMetaDataTopic(oldTopic: Topic, newTopic: Topic) + + fun deleteMetaData(topic: Topic) + + fun existsByTopicAndType(topic: Topic, type: AppMetaDataType): Boolean + + fun getByTopicAndType(topic: Topic, type: AppMetaDataType): AppMetaData? + + fun upsertPeerMetadata(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepository.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepository.kt index 63b38f103..55a62718f 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepository.kt @@ -1,14 +1,14 @@ -package com.walletconnect.android.internal.common.storage.pairing +package com.reown.android.internal.common.storage.pairing import android.database.sqlite.SQLiteException import app.cash.sqldelight.async.coroutines.awaitAsList -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.android.sdk.storage.data.dao.PairingQueries -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.Redirect +import com.reown.android.sdk.storage.data.dao.PairingQueries +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty class PairingStorageRepository(private val pairingQueries: PairingQueries) : PairingStorageRepositoryInterface { diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt new file mode 100644 index 000000000..2f2039b25 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt @@ -0,0 +1,24 @@ +package com.reown.android.internal.common.storage.pairing + +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Pairing +import com.reown.foundation.common.model.Topic + +interface PairingStorageRepositoryInterface { + + fun insertPairing(pairing: Pairing) + + fun deletePairing(topic: Topic) + + fun hasTopic(topic: Topic): Boolean + + suspend fun getListOfPairings(): List + + suspend fun getListOfPairingsWithoutRequestReceived(): List + + fun setRequestReceived(topic: Topic) + + fun updateExpiry(topic: Topic, expiry: Expiry) + + fun getPairingOrNullByTopic(topic: Topic): Pairing? +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/push_messages/PushMessagesRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/push_messages/PushMessagesRepository.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/push_messages/PushMessagesRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/push_messages/PushMessagesRepository.kt index 107733746..b946747c1 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/push_messages/PushMessagesRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/push_messages/PushMessagesRepository.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.android.internal.common.storage.push_messages +package com.reown.android.internal.common.storage.push_messages import android.database.sqlite.SQLiteException -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.sdk.storage.data.dao.PushMessageQueries +import com.reown.android.internal.common.model.Tags +import com.reown.android.sdk.storage.data.dao.PushMessageQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -13,7 +13,7 @@ import kotlinx.coroutines.withContext class PushMessagesRepository(private val pushMessageQueries: PushMessageQueries) { - val notificationTags = listOf(Tags.SESSION_PROPOSE.id, Tags.SESSION_REQUEST.id, Tags.AUTH_REQUEST.id, Tags.NOTIFY_MESSAGE.id, Tags.SESSION_AUTHENTICATE.id) + val notificationTags = listOf(Tags.SESSION_PROPOSE.id, Tags.SESSION_REQUEST.id, Tags.NOTIFY_MESSAGE.id, Tags.SESSION_AUTHENTICATE.id) private val _arePushNotificationsEnabled: MutableStateFlow = MutableStateFlow(false) val arePushNotificationsEnabled: StateFlow = _arePushNotificationsEnabled.asStateFlow() diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/rpc/JsonRpcHistory.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/rpc/JsonRpcHistory.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/rpc/JsonRpcHistory.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/rpc/JsonRpcHistory.kt index 4a6d96176..85f286a19 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/rpc/JsonRpcHistory.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/rpc/JsonRpcHistory.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.internal.common.storage.rpc +package com.reown.android.internal.common.storage.rpc -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.sdk.storage.data.dao.JsonRpcHistoryQueries -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.model.TransportType +import com.reown.android.sdk.storage.data.dao.JsonRpcHistoryQueries +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger class JsonRpcHistory( private val jsonRpcHistoryQueries: JsonRpcHistoryQueries, diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/verify/VerifyContextStorageRepository.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/verify/VerifyContextStorageRepository.kt similarity index 80% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/verify/VerifyContextStorageRepository.kt rename to core/android/src/main/kotlin/com/reown/android/internal/common/storage/verify/VerifyContextStorageRepository.kt index 279d66daf..5d7709d92 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/verify/VerifyContextStorageRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/storage/verify/VerifyContextStorageRepository.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.internal.common.storage.verify +package com.reown.android.internal.common.storage.verify import android.database.sqlite.SQLiteException -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.sdk.storage.data.dao.VerifyContextQueries -import com.walletconnect.android.verify.model.VerifyContext +import com.reown.android.internal.common.model.Validation +import com.reown.android.sdk.storage.data.dao.VerifyContextQueries +import com.reown.android.verify.model.VerifyContext class VerifyContextStorageRepository(private val verifyContextQueries: VerifyContextQueries) { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Context.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/Context.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Context.kt rename to core/android/src/main/kotlin/com/reown/android/internal/utils/Context.kt index 3e47b5f3d..905d69c56 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Context.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/Context.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.internal.utils +package com.reown.android.internal.utils -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.Topic private const val SELF_PARTICIPANT_CONTEXT = "self_participant/" private const val SELF_INVITE_PUBLIC_KEY_CONTEXT = "self_inviteKey/" diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/CoreValidator.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/CoreValidator.kt similarity index 94% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/utils/CoreValidator.kt rename to core/android/src/main/kotlin/com/reown/android/internal/utils/CoreValidator.kt index 1e5360ace..3b0775c97 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/CoreValidator.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/CoreValidator.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.internal.utils +package com.reown.android.internal.utils -import com.walletconnect.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Expiry object CoreValidator { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Expiration.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/Expiration.kt similarity index 78% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Expiration.kt rename to core/android/src/main/kotlin/com/reown/android/internal/utils/Expiration.kt index 0933f9e52..b1acf97e2 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Expiration.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/Expiration.kt @@ -1,6 +1,6 @@ @file:JvmName("Expiration") -package com.walletconnect.android.internal.utils +package com.reown.android.internal.utils val PROPOSAL_EXPIRY: Long get() = currentTimeInSeconds + fiveMinutesInSeconds val ACTIVE_SESSION: Long get() = currentTimeInSeconds + weekInSeconds \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/utils/ObservableMap.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/ObservableMap.kt new file mode 100644 index 000000000..5322c15b0 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/ObservableMap.kt @@ -0,0 +1,19 @@ +package com.reown.android.internal.utils + +class ObservableMap( + private val map: MutableMap = mutableMapOf(), + private val onChange: (Map) -> Unit +) : MutableMap by map { + + override fun put(key: K, value: V): V? { + return map.put(key, value).also { onChange(map) } + } + + override fun remove(key: K): V? { + return map.remove(key).also { onChange(map) } + } + + override fun putAll(from: Map) { + return map.putAll(from).also { onChange(map) } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Time.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/Time.kt similarity index 92% rename from core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Time.kt rename to core/android/src/main/kotlin/com/reown/android/internal/utils/Time.kt index 95f9cdc10..c3138e7b9 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/Time.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/Time.kt @@ -1,6 +1,6 @@ @file:JvmName("Time") -package com.walletconnect.android.internal.utils +package com.reown.android.internal.utils import java.util.concurrent.TimeUnit diff --git a/core/android/src/main/kotlin/com/reown/android/internal/utils/UtilFunctions.kt b/core/android/src/main/kotlin/com/reown/android/internal/utils/UtilFunctions.kt new file mode 100644 index 000000000..6a66f5974 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/internal/utils/UtilFunctions.kt @@ -0,0 +1,60 @@ +@file:Suppress("PackageDirectoryMismatch") + +package com.reown.utils + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.type.SerializableJsonRpc +import com.reown.android.internal.utils.currentTimeInSeconds +import org.koin.core.definition.KoinDefinition +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.ext.getFullName +import java.net.URI +import kotlin.reflect.KClass + +@get:JvmSynthetic +val String.Companion.Empty + get() = "" + +@get:JvmSynthetic +val Int.Companion.DefaultId + get() = -1 + +@JvmSynthetic +fun Long.extractTimestamp() = this / 1000 + +@JvmSynthetic +fun Expiry.isSequenceValid(): Boolean = seconds > currentTimeInSeconds + +@get:JvmSynthetic +val String.Companion.HexPrefix + get() = "0x" + +fun Module.addSerializerEntry(value: KClass): KoinDefinition> = + single(qualifier = named("key_${value.getFullName()}")) { value } + +fun Module.addDeserializerEntry(key: String, value: KClass<*>): KoinDefinition>> = + single(qualifier = named("${key}_${value.getFullName()}")) { key to value } + +// Had to add JsonAdapterEntry because Koin would fetch the wrong values when using Pair instead +data class JsonAdapterEntry(val type: Class, val adapter: (Moshi) -> JsonAdapter) { + override fun toString(): String = "JsonAdapterEntry(type=${type.name})" +} + +fun Module.addJsonAdapter(type: Class, adapter: (Moshi) -> JsonAdapter): KoinDefinition> { + val jsonAdapterEntry = JsonAdapterEntry(type, adapter) + return single(qualifier = named("$jsonAdapterEntry")) { jsonAdapterEntry } +} + +@JvmSynthetic +fun compareDomains(metadataUrl: String, originUrl: String): Boolean { + try { + val metadataDomain = URI(metadataUrl).host.removePrefix("www.") + val originDomain = URI(originUrl).host.removePrefix("www.") + return metadataDomain == originDomain + } catch (e: Exception) { + return false + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/data/service/KeyServerService.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/data/service/KeyServerService.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/keyserver/data/service/KeyServerService.kt rename to core/android/src/main/kotlin/com/reown/android/keyserver/data/service/KeyServerService.kt index d9404e9a8..522f6f0fa 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/data/service/KeyServerService.kt +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/data/service/KeyServerService.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.android.keyserver.data.service +package com.reown.android.keyserver.data.service -import com.walletconnect.android.keyserver.model.KeyServerBody -import com.walletconnect.android.keyserver.model.KeyServerHttpResponse +import com.reown.android.keyserver.model.KeyServerBody +import com.reown.android.keyserver.model.KeyServerHttpResponse import retrofit2.Response import retrofit2.http.* diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/IdentitiesInteractor.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/IdentitiesInteractor.kt similarity index 78% rename from core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/IdentitiesInteractor.kt rename to core/android/src/main/kotlin/com/reown/android/keyserver/domain/IdentitiesInteractor.kt index d500efea8..bf24255ea 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/IdentitiesInteractor.kt +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/IdentitiesInteractor.kt @@ -1,39 +1,39 @@ -package com.walletconnect.android.keyserver.domain - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.AccountHasDifferentStatementStored -import com.walletconnect.android.internal.common.exception.AccountHasNoCacaoPayloadStored -import com.walletconnect.android.internal.common.exception.AccountHasNoIdentityStored -import com.walletconnect.android.internal.common.exception.InvalidAccountIdException -import com.walletconnect.android.internal.common.exception.InvalidIdentityCacao -import com.walletconnect.android.internal.common.exception.UserRejectedSigning -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.EncodeIdentityKeyDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.DidJwt -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.signing.cacao.getStatement -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.android.internal.common.storage.identity.IdentitiesStorageRepository -import com.walletconnect.android.internal.utils.getIdentityTag -import com.walletconnect.android.keyserver.domain.use_case.RegisterIdentityUseCase -import com.walletconnect.android.keyserver.domain.use_case.ResolveIdentityUseCase -import com.walletconnect.android.keyserver.domain.use_case.UnregisterIdentityUseCase -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.DID_DELIMITER -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes +package com.reown.android.keyserver.domain + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.AccountHasDifferentStatementStored +import com.reown.android.internal.common.exception.AccountHasNoCacaoPayloadStored +import com.reown.android.internal.common.exception.AccountHasNoIdentityStored +import com.reown.android.internal.common.exception.InvalidAccountIdException +import com.reown.android.internal.common.exception.InvalidIdentityCacao +import com.reown.android.internal.common.exception.UserRejectedSigning +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.jwt.did.EncodeIdentityKeyDidJwtPayloadUseCase +import com.reown.android.internal.common.jwt.did.encodeDidJwt +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.DidJwt +import com.reown.android.internal.common.model.MissingKeyException +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.common.signing.cacao.getStatement +import com.reown.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.internal.common.storage.identity.IdentitiesStorageRepository +import com.reown.android.internal.utils.getIdentityTag +import com.reown.android.keyserver.domain.use_case.RegisterIdentityUseCase +import com.reown.android.keyserver.domain.use_case.ResolveIdentityUseCase +import com.reown.android.keyserver.domain.use_case.UnregisterIdentityUseCase +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.DID_DELIMITER +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeEd25519DidKey +import com.reown.util.bytesToHex +import com.reown.util.randomBytes import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt new file mode 100644 index 000000000..6c0daaaf8 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt @@ -0,0 +1,13 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerBody + +class RegisterIdentityUseCase( + private val service: KeyServerService, +) { + suspend operator fun invoke(cacao: Cacao): Result = runCatching { + service.registerIdentity(KeyServerBody.RegisterIdentity(cacao)).unwrapUnit() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterInviteUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterInviteUseCase.kt new file mode 100644 index 000000000..945167670 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/RegisterInviteUseCase.kt @@ -0,0 +1,12 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerBody + +class RegisterInviteUseCase( + private val service: KeyServerService, +) { + suspend operator fun invoke(idAuth: String): Result = runCatching { + service.registerInvite(KeyServerBody.RegisterInvite(idAuth)).unwrapUnit() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt new file mode 100644 index 000000000..eed67fba1 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt @@ -0,0 +1,12 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerResponse + +class ResolveIdentityUseCase( + private val service: KeyServerService, +) { + suspend operator fun invoke(identityKey: String): Result = runCatching { + service.resolveIdentity(identityKey).unwrapValue() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveInviteUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveInviteUseCase.kt new file mode 100644 index 000000000..e377f8bc7 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/ResolveInviteUseCase.kt @@ -0,0 +1,13 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.internal.common.model.AccountId +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerResponse + +class ResolveInviteUseCase( + private val service: KeyServerService +) { + suspend operator fun invoke(accountId: AccountId): Result = runCatching { + service.resolveInvite(accountId.value).unwrapValue() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt new file mode 100644 index 000000000..a02e34ce9 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt @@ -0,0 +1,12 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerBody + +class UnregisterIdentityUseCase( + private val service: KeyServerService, +) { + suspend operator fun invoke(idAuth: String): Result = runCatching { + service.unregisterIdentity(KeyServerBody.UnregisterIdentity(idAuth)).unwrapUnit() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt new file mode 100644 index 000000000..c8a264d9b --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt @@ -0,0 +1,12 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.keyserver.data.service.KeyServerService +import com.reown.android.keyserver.model.KeyServerBody + +class UnregisterInviteUseCase( + private val service: KeyServerService, +) { + suspend operator fun invoke(idAuth: String): Result = runCatching { + service.unregisterInvite(KeyServerBody.UnregisterInvite(idAuth)).unwrapUnit() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/Utils.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/Utils.kt new file mode 100644 index 000000000..e0b4715ce --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/domain/use_case/Utils.kt @@ -0,0 +1,36 @@ +package com.reown.android.keyserver.domain.use_case + +import com.reown.android.keyserver.model.KeyServerHttpResponse +import com.reown.android.keyserver.model.KeyServerHttpResponse.Companion.SUCCESS_STATUS +import com.reown.android.keyserver.model.KeyServerResponse +import retrofit2.Response + +@JvmSynthetic +internal fun > Response.unwrapUnit() { + if (isSuccessful && body() != null) { + if (body()!!.status == SUCCESS_STATUS) { + return + } else { + throw Throwable(body()!!.error?.message) + } + } else { + throw Throwable(errorBody()?.string()) + } +} + +@JvmSynthetic +internal fun > Response.unwrapValue(): K { + if (isSuccessful && body() != null) { + if (body()!!.status == SUCCESS_STATUS) { + if (body()!!.value != null) { + return body()!!.value!! + } else { + throw Throwable("Expected value is null") + } + } else { + throw Throwable(body()!!.error?.message) + } + } else { + throw Throwable(errorBody()?.string()) + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerBody.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerBody.kt similarity index 80% rename from core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerBody.kt rename to core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerBody.kt index 9fe149557..0e8083ff9 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerBody.kt +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerBody.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.keyserver.model +package com.reown.android.keyserver.model import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.Cacao sealed class KeyServerBody { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerHttpResponse.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerHttpResponse.kt similarity index 97% rename from core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerHttpResponse.kt rename to core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerHttpResponse.kt index 140a5006e..32c0e2ef7 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerHttpResponse.kt +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerHttpResponse.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.keyserver.model +package com.reown.android.keyserver.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerResponse.kt b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerResponse.kt new file mode 100644 index 000000000..7a0eabcd3 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/keyserver/model/KeyServerResponse.kt @@ -0,0 +1,13 @@ +package com.reown.android.keyserver.model + +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.signing.cacao.Cacao + +sealed class KeyServerResponse { + + @JsonClass(generateAdapter = true) + data class ResolveInvite(val inviteKey: String) : KeyServerResponse() + + @JsonClass(generateAdapter = true) + data class ResolveIdentity(val cacao: Cacao) : KeyServerResponse() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingInterface.kt b/core/android/src/main/kotlin/com/reown/android/pairing/client/PairingInterface.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingInterface.kt rename to core/android/src/main/kotlin/com/reown/android/pairing/client/PairingInterface.kt index 2bcb08eae..65c89e80f 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/pairing/client/PairingInterface.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.pairing.client +package com.reown.android.pairing.client -import com.walletconnect.android.Core +import com.reown.android.Core interface PairingInterface { @@ -30,7 +30,7 @@ interface PairingInterface { @Deprecated(message = "Disconnect method has been deprecated. It will be removed soon. Pairing will disconnect automatically internally.") fun disconnect(disconnect: Core.Params.Disconnect, onError: (Core.Model.Error) -> Unit = {}) - @Deprecated(message = "Ping method has been deprecated. It will be removed soon. Please use Ping from Web3Wallet or Sign clients.") + @Deprecated(message = "Ping method has been deprecated. It will be removed soon. Please use Ping from WalletKit or Sign clients.") fun ping(ping: Core.Params.Ping, pairingPing: Core.Listeners.PairingPing? = null) /** diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/client/PairingProtocol.kt b/core/android/src/main/kotlin/com/reown/android/pairing/client/PairingProtocol.kt new file mode 100644 index 000000000..1a9deebec --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/client/PairingProtocol.kt @@ -0,0 +1,141 @@ +@file:JvmSynthetic + +package com.reown.android.pairing.client + +import com.reown.android.Core +import com.reown.android.internal.Validator +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pairing.engine.domain.PairingEngine +import com.reown.android.pairing.engine.model.EngineDO +import com.reown.android.pairing.model.mapper.toCore +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.relay.RelayConnectionInterface +import com.reown.foundation.util.Logger +import kotlinx.coroutines.flow.* +import org.koin.core.KoinApplication + +internal class PairingProtocol(private val koinApp: KoinApplication = wcKoinApp) : PairingInterface { + private lateinit var pairingEngine: PairingEngine + private val logger: Logger by lazy { koinApp.koin.get() } + private val relayClient: RelayConnectionInterface by lazy { koinApp.koin.get() } + private val insertEventUseCase: InsertTelemetryEventUseCase by lazy { koinApp.koin.get() } + + override fun initialize() { + pairingEngine = koinApp.koin.get() + } + + override fun setDelegate(delegate: PairingInterface.Delegate) { + checkEngineInitialization() + + pairingEngine.engineEvent.onEach { event -> + when (event) { + is EngineDO.PairingDelete -> delegate.onPairingDelete(event.toCore()) + is EngineDO.PairingExpire -> delegate.onPairingExpired(Core.Model.ExpiredPairing(event.pairing.toCore())) + is EngineDO.PairingState -> delegate.onPairingState(Core.Model.PairingState(event.isPairingState)) + } + }.launchIn(scope) + } + + @Throws(IllegalStateException::class) + override fun create(onError: (Core.Model.Error) -> Unit): Core.Model.Pairing? { + checkEngineInitialization() + + return try { + pairingEngine.create({ error -> onError(Core.Model.Error(error)) }) + } catch (e: Exception) { + onError(Core.Model.Error(e)) + null + } + } + + @Throws(IllegalStateException::class) + override fun create(onError: (Core.Model.Error) -> Unit, methods: String): Core.Model.Pairing? { + checkEngineInitialization() + + return try { + pairingEngine.create({ error -> onError(Core.Model.Error(error)) }, methods) + } catch (e: Exception) { + onError(Core.Model.Error(e)) + null + } + } + + @Throws(IllegalStateException::class) + override fun pair( + pair: Core.Params.Pair, + onSuccess: (Core.Params.Pair) -> Unit, + onError: (Core.Model.Error) -> Unit, + ) { + checkEngineInitialization() + try { + pairingEngine.pair( + uri = pair.uri, + onSuccess = { onSuccess(pair) }, + onFailure = { error -> onError(Core.Model.Error(Throwable("Pairing error: ${error.message}"))) } + ) + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + @Deprecated(message = "Disconnect method has been deprecated. It will be removed soon. Pairing will disconnect automatically internally.") + @Throws(IllegalStateException::class) + override fun disconnect(disconnect: Core.Params.Disconnect, onError: (Core.Model.Error) -> Unit) { + checkEngineInitialization() + + try { + pairingEngine.disconnect(disconnect.topic) { error -> onError(Core.Model.Error(error)) } + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + @Deprecated(message = "Disconnect method has been deprecated. It will be removed soon. Pairing will disconnect automatically internally.") + @Throws(IllegalStateException::class) + override fun disconnect(topic: String, onError: (Core.Model.Error) -> Unit) { + checkEngineInitialization() + + try { + pairingEngine.disconnect(topic) { error -> onError(Core.Model.Error(error)) } + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + @Deprecated(message = "Ping method has been deprecated. It will be removed soon. Please use Ping from WalletKit or Sign clients.") + @Throws(IllegalStateException::class) + override fun ping(ping: Core.Params.Ping, pairingPing: Core.Listeners.PairingPing?) { + checkEngineInitialization() + + try { + pairingEngine.ping(ping.topic, + onSuccess = { topic -> pairingPing?.onSuccess(Core.Model.Ping.Success(topic)) }, + onFailure = { error -> pairingPing?.onError(Core.Model.Ping.Error(error)) }) + } catch (e: Exception) { + pairingPing?.onError(Core.Model.Ping.Error(e)) + } + } + + @Throws(IllegalStateException::class) + override fun getPairings(): List { + checkEngineInitialization() + + return pairingEngine.getPairings().map { pairing -> pairing.toCore() } + } + + override fun validatePairingUri(uri: String): Boolean { + return try { + Validator.validateWCUri(uri) != null + } catch (e: Exception) { + false + } + } + + @Throws(IllegalStateException::class) + private fun checkEngineInitialization() { + check(::pairingEngine.isInitialized) { + "CoreClient needs to be initialized first using the initialize function" + } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/domain/PairingEngine.kt b/core/android/src/main/kotlin/com/reown/android/pairing/engine/domain/PairingEngine.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/domain/PairingEngine.kt rename to core/android/src/main/kotlin/com/reown/android/pairing/engine/domain/PairingEngine.kt index d6b3c3c51..d11e594d5 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/domain/PairingEngine.kt +++ b/core/android/src/main/kotlin/com/reown/android/pairing/engine/domain/PairingEngine.kt @@ -1,56 +1,56 @@ -package com.walletconnect.android.pairing.engine.domain - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.MALFORMED_PAIRING_URI_MESSAGE -import com.walletconnect.android.internal.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.android.internal.Validator -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.exception.ExpiredPairingException -import com.walletconnect.android.internal.common.exception.ExpiredPairingURIException -import com.walletconnect.android.internal.common.exception.Invalid -import com.walletconnect.android.internal.common.exception.MalformedWalletConnectUri -import com.walletconnect.android.internal.common.exception.NoInternetConnectionException -import com.walletconnect.android.internal.common.exception.NoRelayConnectionException -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.WalletConnectUri -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.pairing.PairingStorageRepositoryInterface -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.android.pairing.engine.model.EngineDO -import com.walletconnect.android.pairing.model.PairingJsonRpcMethod -import com.walletconnect.android.pairing.model.PairingParams -import com.walletconnect.android.pairing.model.PairingRpc -import com.walletconnect.android.pairing.model.mapper.toCore -import com.walletconnect.android.pairing.model.pairingExpiry -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.domain.SendBatchEventUseCase -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.Trace -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes +package com.reown.android.pairing.engine.domain + +import com.reown.android.Core +import com.reown.android.internal.MALFORMED_PAIRING_URI_MESSAGE +import com.reown.android.internal.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.android.internal.Validator +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.exception.ExpiredPairingException +import com.reown.android.internal.common.exception.ExpiredPairingURIException +import com.reown.android.internal.common.exception.Invalid +import com.reown.android.internal.common.exception.MalformedWalletConnectUri +import com.reown.android.internal.common.exception.NoInternetConnectionException +import com.reown.android.internal.common.exception.NoRelayConnectionException +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.WalletConnectUri +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.pairing.PairingStorageRepositoryInterface +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.internal.utils.thirtySeconds +import com.reown.android.pairing.engine.model.EngineDO +import com.reown.android.pairing.model.PairingJsonRpcMethod +import com.reown.android.pairing.model.PairingParams +import com.reown.android.pairing.model.PairingRpc +import com.reown.android.pairing.model.mapper.toCore +import com.reown.android.pairing.model.pairingExpiry +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.domain.SendBatchEventUseCase +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.Trace +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.util.bytesToHex +import com.reown.util.randomBytes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -63,7 +63,6 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -84,8 +83,11 @@ internal class PairingEngine( private val crypto: KeyManagementRepository, private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, private val pairingRepository: PairingStorageRepositoryInterface, - private val insertEventUseCase: InsertTelemetryEventUseCase, - private val sendBatchEventUseCase: SendBatchEventUseCase + private val insertTelemetryEventUseCase: InsertTelemetryEventUseCase, + private val insertEventUseCase: InsertEventUseCase, + private val sendBatchEventUseCase: SendBatchEventUseCase, + private val clientId: String, + private val userAgent: String ) { private var jsonRpcRequestsJob: Job? = null private val setOfRegisteredMethods: MutableSet = mutableSetOf() @@ -160,7 +162,7 @@ internal class PairingEngine( val trace: MutableList = mutableListOf() trace.add(Trace.Pairing.PAIRING_STARTED).also { logger.log("Pairing started") } val walletConnectUri: WalletConnectUri = Validator.validateWCUri(uri) ?: run { - scope.launch { supervisorScope { insertEventUseCase(Props(type = EventType.Error.MALFORMED_PAIRING_URI, properties = Properties(trace = trace))) } } + scope.launch { supervisorScope { insertTelemetryEventUseCase(Props(type = EventType.Error.MALFORMED_PAIRING_URI, properties = Properties(trace = trace))) } } return onFailure(MalformedWalletConnectUri(MALFORMED_PAIRING_URI_MESSAGE)) } trace.add(Trace.Pairing.PAIRING_URI_VALIDATION_SUCCESS) @@ -169,7 +171,7 @@ internal class PairingEngine( val symmetricKey = walletConnectUri.symKey try { if (walletConnectUri.expiry?.isExpired() == true) { - scope.launch { supervisorScope { insertEventUseCase(Props(type = EventType.Error.PAIRING_URI_EXPIRED, properties = Properties(trace = trace, topic = pairingTopic.value))) } } + scope.launch { supervisorScope { insertTelemetryEventUseCase(Props(type = EventType.Error.PAIRING_URI_EXPIRED, properties = Properties(trace = trace, topic = pairingTopic.value))) } } .also { logger.error("Pairing URI expired: $pairingTopic") } return onFailure(ExpiredPairingURIException("Pairing URI expired: $pairingTopic")) } @@ -178,7 +180,7 @@ internal class PairingEngine( val localPairing = pairingRepository.getPairingOrNullByTopic(pairingTopic) trace.add(Trace.Pairing.EXISTING_PAIRING) if (!localPairing!!.isNotExpired()) { - scope.launch { supervisorScope { insertEventUseCase(Props(type = EventType.Error.PAIRING_EXPIRED, properties = Properties(trace = trace, topic = pairingTopic.value))) } } + scope.launch { supervisorScope { insertTelemetryEventUseCase(Props(type = EventType.Error.PAIRING_EXPIRED, properties = Properties(trace = trace, topic = pairingTopic.value))) } } .also { logger.error("Pairing expired: $pairingTopic") } return onFailure(ExpiredPairingException("Pairing expired: ${pairingTopic.value}")) } @@ -202,7 +204,7 @@ internal class PairingEngine( }, onFailure = { error -> scope.launch { supervisorScope { - insertEventUseCase(Props(type = EventType.Error.PAIRING_SUBSCRIPTION_FAILURE, properties = Properties(trace = trace, topic = pairingTopic.value))) + insertTelemetryEventUseCase(Props(type = EventType.Error.PAIRING_SUBSCRIPTION_FAILURE, properties = Properties(trace = trace, topic = pairingTopic.value))) } }.also { logger.error("Subscribe pairing topic error: $pairingTopic, error: $error") } onFailure(error) @@ -211,9 +213,18 @@ internal class PairingEngine( } catch (e: Exception) { logger.error("Subscribe pairing topic error: $pairingTopic, error: $e") if (e is NoRelayConnectionException) - scope.launch { supervisorScope { insertEventUseCase(Props(type = EventType.Error.NO_WSS_CONNECTION, properties = Properties(trace = trace, topic = pairingTopic.value))) } } + scope.launch { supervisorScope { insertTelemetryEventUseCase(Props(type = EventType.Error.NO_WSS_CONNECTION, properties = Properties(trace = trace, topic = pairingTopic.value))) } } if (e is NoInternetConnectionException) - scope.launch { supervisorScope { insertEventUseCase(Props(type = EventType.Error.NO_INTERNET_CONNECTION, properties = Properties(trace = trace, topic = pairingTopic.value))) } } + scope.launch { + supervisorScope { + insertTelemetryEventUseCase( + Props( + type = EventType.Error.NO_INTERNET_CONNECTION, + properties = Properties(trace = trace, topic = pairingTopic.value) + ) + ) + } + } runCatching { crypto.removeKeys(pairingTopic.value) }.onFailure { logger.error("Remove keys error: $pairingTopic, error: $it") } jsonRpcInteractor.unsubscribe(pairingTopic) onFailure(e) @@ -288,6 +299,7 @@ internal class PairingEngine( scope.launch { supervisorScope { try { + insertEventUseCase(Props(event = EventType.INIT, properties = Properties(clientId = clientId, userAgent = userAgent))) sendBatchEventUseCase() } catch (e: Exception) { logger.error("Error when sending events: $e") @@ -297,8 +309,7 @@ internal class PairingEngine( } private fun resubscribeToPairingTopics() { - jsonRpcInteractor.wssConnectionState - .filterIsInstance() + jsonRpcInteractor.onResubscribe .onEach { supervisorScope { launch(Dispatchers.IO) { diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/engine/model/EngineDO.kt b/core/android/src/main/kotlin/com/reown/android/pairing/engine/model/EngineDO.kt new file mode 100644 index 000000000..68d3d8c57 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/engine/model/EngineDO.kt @@ -0,0 +1,23 @@ +@file:JvmSynthetic + +package com.reown.android.pairing.engine.model + +import com.reown.android.internal.common.model.Pairing + +internal sealed class EngineDO { + + @Deprecated(message = "This data object has been deprecated. It will be removed soon.") + data class PairingDelete( + val topic: String, + val reason: String, + ) : EngineDO() + + @Deprecated(message = "This data object has been deprecated. It will be removed soon.") + data class PairingExpire( + val pairing: Pairing + ) : EngineDO() + + data class PairingState( + val isPairingState: Boolean + ) : EngineDO() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingController.kt b/core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingController.kt similarity index 85% rename from core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingController.kt rename to core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingController.kt index 6cb9a20c9..09b34f22c 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingController.kt +++ b/core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingController.kt @@ -1,12 +1,12 @@ -package com.walletconnect.android.pairing.handler +package com.reown.android.pairing.handler -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pairing.engine.domain.PairingEngine -import com.walletconnect.android.pairing.model.mapper.toAppMetaData -import com.walletconnect.foundation.common.model.Topic +import com.reown.android.Core +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pairing.engine.domain.PairingEngine +import com.reown.android.pairing.model.mapper.toAppMetaData +import com.reown.foundation.common.model.Topic import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.merge diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingControllerInterface.kt b/core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingControllerInterface.kt new file mode 100644 index 000000000..42ac8802c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/handler/PairingControllerInterface.kt @@ -0,0 +1,26 @@ +package com.reown.android.pairing.handler + +import com.reown.android.Core +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.SDKError +import com.reown.foundation.common.model.Topic +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow + +interface PairingControllerInterface { + val findWrongMethodsFlow: Flow + val storedPairingFlow: SharedFlow>> + val checkVerifyKeyFlow: SharedFlow + + fun initialize() + + fun setRequestReceived(activate: Core.Params.RequestReceived, onError: (Core.Model.Error) -> Unit = {}) + + fun updateMetadata(updateMetadata: Core.Params.UpdateMetadata, onError: (Core.Model.Error) -> Unit = {}) + + fun deleteAndUnsubscribePairing(deletePairing: Core.Params.Delete, onError: (Core.Model.Error) -> Unit = {}) + + fun register(vararg method: String) + + fun getPairingByTopic(topic: Topic): Pairing? +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingExpiration.kt b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingExpiration.kt new file mode 100644 index 000000000..f913075d9 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingExpiration.kt @@ -0,0 +1,8 @@ +@file:JvmName("Expiration") + +package com.reown.android.pairing.model + +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.fiveMinutesInSeconds + +val pairingExpiry: Long get() = currentTimeInSeconds + fiveMinutesInSeconds \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingJsonRpcMethod.kt b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingJsonRpcMethod.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingJsonRpcMethod.kt rename to core/android/src/main/kotlin/com/reown/android/pairing/model/PairingJsonRpcMethod.kt index e4cd81520..c2369f32d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingJsonRpcMethod.kt +++ b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingJsonRpcMethod.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.pairing.model +package com.reown.android.pairing.model object PairingJsonRpcMethod { @JvmSynthetic diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingParams.kt b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingParams.kt new file mode 100644 index 000000000..2594c19d5 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingParams.kt @@ -0,0 +1,20 @@ +package com.reown.android.pairing.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.utils.DefaultId + +sealed class PairingParams : ClientParams { + + @JsonClass(generateAdapter = true) + class DeleteParams( + @Json(name = "code") + val code: Int = Int.DefaultId, + @Json(name = "message") + val message: String, + ) : PairingParams() + + @Suppress("CanSealedSubClassBeObject") + class PingParams : PairingParams() +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingRpc.kt b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingRpc.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingRpc.kt rename to core/android/src/main/kotlin/com/reown/android/pairing/model/PairingRpc.kt index 64608acdc..98a86d793 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingRpc.kt +++ b/core/android/src/main/kotlin/com/reown/android/pairing/model/PairingRpc.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.pairing.model +package com.reown.android.pairing.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.util.generateId +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.util.generateId internal sealed class PairingRpc : JsonRpcClientSync { diff --git a/core/android/src/main/kotlin/com/reown/android/pairing/model/mapper/PairingMapper.kt b/core/android/src/main/kotlin/com/reown/android/pairing/model/mapper/PairingMapper.kt new file mode 100644 index 000000000..3193bfb5c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pairing/model/mapper/PairingMapper.kt @@ -0,0 +1,47 @@ +package com.reown.android.pairing.model.mapper + +import com.reown.android.Core +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.Redirect +import com.reown.android.pairing.engine.model.EngineDO +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty + +@JvmSynthetic +@Deprecated("This mapper has been deprecated. It will be removed soon.") +internal fun EngineDO.PairingDelete.toCore(): Core.Model.DeletedPairing = + Core.Model.DeletedPairing(topic, reason) + +@JvmSynthetic +@Deprecated("This mapper has been deprecated. It will be removed soon.") +internal fun Pairing.toCore(): Core.Model.Pairing = + Core.Model.Pairing( + topic.value, + expiry.seconds, + peerAppMetaData?.toCore(), + relayProtocol, + relayData, + uri, + isActive = true, + methods ?: String.Empty + ) + +@JvmSynthetic +fun Core.Model.Pairing.toPairing(): Pairing = + Pairing( + Topic(topic), + Expiry(expiry), + peerAppMetaData?.toAppMetaData(), + relayProtocol, + relayData, + uri, + methods = registeredMethods + ) + +@JvmSynthetic +internal fun Core.Model.AppMetaData.toAppMetaData() = AppMetaData(name = name, description = description, url = url, icons = icons, redirect = Redirect(redirect)) + +@JvmSynthetic +internal fun AppMetaData?.toCore() = Core.Model.AppMetaData(this?.name ?: String.Empty, this?.description ?: String.Empty, this?.url ?: String.Empty, this?.icons ?: emptyList(), this?.redirect?.native) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/data/PulseService.kt b/core/android/src/main/kotlin/com/reown/android/pulse/data/PulseService.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/data/PulseService.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/data/PulseService.kt index d3e13c666..d9bf4557d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/data/PulseService.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/data/PulseService.kt @@ -1,8 +1,8 @@ @file:JvmSynthetic -package com.walletconnect.android.pulse.data +package com.reown.android.pulse.data -import com.walletconnect.android.pulse.model.Event +import com.reown.android.pulse.model.Event import retrofit2.Response import retrofit2.http.Body import retrofit2.http.Header diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/InsertTelemetryEventUseCase.kt b/core/android/src/main/kotlin/com/reown/android/pulse/domain/InsertTelemetryEventUseCase.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/InsertTelemetryEventUseCase.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/domain/InsertTelemetryEventUseCase.kt index ff6858fcb..99530dfde 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/InsertTelemetryEventUseCase.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/domain/InsertTelemetryEventUseCase.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.pulse.domain +package com.reown.android.pulse.domain -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendBatchEventUseCase.kt b/core/android/src/main/kotlin/com/reown/android/pulse/domain/SendBatchEventUseCase.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendBatchEventUseCase.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/domain/SendBatchEventUseCase.kt index d8dd89cb8..1b31f6e7a 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendBatchEventUseCase.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/domain/SendBatchEventUseCase.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.pulse.domain +package com.reown.android.pulse.domain -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.data.PulseService -import com.walletconnect.android.pulse.model.Event -import com.walletconnect.android.pulse.model.SDKType -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.data.PulseService +import com.reown.android.pulse.model.Event +import com.reown.android.pulse.model.SDKType +import com.reown.foundation.util.Logger import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/core/android/src/main/kotlin/com/reown/android/pulse/domain/SendEventUseCase.kt b/core/android/src/main/kotlin/com/reown/android/pulse/domain/SendEventUseCase.kt new file mode 100644 index 000000000..b10a90c25 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pulse/domain/SendEventUseCase.kt @@ -0,0 +1,47 @@ +package com.reown.android.pulse.domain + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.pulse.data.PulseService +import com.reown.android.pulse.model.Event +import com.reown.android.pulse.model.SDKType +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.util.generateId +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import org.koin.core.qualifier.named + +class SendEventUseCase( + private val pulseService: PulseService, + private val logger: Logger, + private val bundleId: String, +) : SendEventInterface { + private val enableW3MAnalytics: Boolean by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.ENABLE_WEB_3_MODAL_ANALYTICS)) } + + override fun send(props: Props, sdkType: SDKType, timestamp: Long?, id: Long?) { + if (enableW3MAnalytics) { + scope.launch { + supervisorScope { + try { + val event = Event(props = props, bundleId = bundleId, timestamp = timestamp ?: currentTimeInSeconds, eventId = id ?: generateId()) + val response = pulseService.sendEvent(body = event, sdkType = sdkType.type) + if (!response.isSuccessful) { + logger.error("Failed to send event: ${event.props.type}") + } else { + logger.log("Event sent successfully: ${event.props.type}") + } + } catch (e: Exception) { + logger.error("Failed to send event: ${props.type}, error: $e") + } + } + } + } + } +} + +interface SendEventInterface { + fun send(props: Props, sdkType: SDKType = SDKType.APPKIT, timestamp: Long? = currentTimeInSeconds, id: Long? = generateId()) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/ConnectionMethod.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/ConnectionMethod.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/model/ConnectionMethod.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/model/ConnectionMethod.kt index 273614418..542ad7593 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/ConnectionMethod.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/ConnectionMethod.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.pulse.model +package com.reown.android.pulse.model object ConnectionMethod { @get:JvmSynthetic diff --git a/core/android/src/main/kotlin/com/reown/android/pulse/model/Direction.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/Direction.kt new file mode 100644 index 000000000..607b84cb0 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/Direction.kt @@ -0,0 +1,6 @@ +package com.reown.android.pulse.model + +enum class Direction(val state: String) { + SENT("sent"), + RECEIVED("received") +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/pulse/model/Event.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/Event.kt new file mode 100644 index 000000000..40b31d4ac --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/Event.kt @@ -0,0 +1,19 @@ +package com.reown.android.pulse.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.pulse.model.properties.Props +import com.reown.util.generateId + +@JsonClass(generateAdapter = true) +data class Event( + @Json(name = "eventId") + val eventId: Long = generateId(), + @Json(name = "bundleId") + val bundleId: String, + @Json(name = "timestamp") + val timestamp: Long = currentTimeInSeconds, + @Json(name = "props") + val props: Props +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/EventType.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/EventType.kt similarity index 97% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/model/EventType.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/model/EventType.kt index 41e0c9a87..badf13d78 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/EventType.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/EventType.kt @@ -1,11 +1,15 @@ -package com.walletconnect.android.pulse.model +package com.reown.android.pulse.model object EventType { @get:JvmSynthetic const val ERROR: String = "ERROR" + @get:JvmSynthetic const val SUCCESS: String = "SUCCESS" + @get:JvmSynthetic + const val INIT: String = "INIT" + @get:JvmSynthetic const val TRACK: String = "TRACE" diff --git a/core/android/src/main/kotlin/com/reown/android/pulse/model/SDKType.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/SDKType.kt new file mode 100644 index 000000000..3114ceb3a --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/SDKType.kt @@ -0,0 +1,6 @@ +package com.reown.android.pulse.model + +enum class SDKType(val type: String) { + APPKIT("appkit"), + EVENTS("events_sdk") +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Trace.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/Trace.kt similarity index 98% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Trace.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/model/Trace.kt index 2bc43be10..ff5eae201 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Trace.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/Trace.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.pulse.model +package com.reown.android.pulse.model object Trace { object Pairing { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Properties.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Properties.kt similarity index 87% rename from core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Properties.kt rename to core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Properties.kt index 0284da3d4..9ac29f82d 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Properties.kt +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Properties.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.pulse.model.properties +package com.reown.android.pulse.model.properties import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -27,4 +27,6 @@ data class Properties( val clientId: String? = null, @Json(name = "direction") val direction: String? = null, + @Json(name = "user_agent") + val userAgent: String? = null, ) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Props.kt b/core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Props.kt new file mode 100644 index 000000000..7c56f297b --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/pulse/model/properties/Props.kt @@ -0,0 +1,14 @@ +package com.reown.android.pulse.model.properties + +import com.squareup.moshi.Json +import com.reown.android.pulse.model.EventType +import com.reown.utils.Empty + +data class Props( + @Json(name = "event") + val event: String = EventType.ERROR, + @Json(name = "type") + val type: String = String.Empty, + @Json(name = "properties") + val properties: Properties? = null +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/PushInterface.kt b/core/android/src/main/kotlin/com/reown/android/push/PushInterface.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/push/PushInterface.kt rename to core/android/src/main/kotlin/com/reown/android/push/PushInterface.kt index 42a9fe661..1a9153c0b 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/PushInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/push/PushInterface.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.push +package com.reown.android.push interface PushInterface { val clientId: String diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/client/PushClient.kt b/core/android/src/main/kotlin/com/reown/android/push/client/PushClient.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/push/client/PushClient.kt rename to core/android/src/main/kotlin/com/reown/android/push/client/PushClient.kt index cd0e71ef9..957750e4f 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/client/PushClient.kt +++ b/core/android/src/main/kotlin/com/reown/android/push/client/PushClient.kt @@ -1,15 +1,15 @@ @file:JvmSynthetic -package com.walletconnect.android.push.client +package com.reown.android.push.client -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.push.PushInterface -import com.walletconnect.android.push.network.PushService -import com.walletconnect.android.push.network.model.PushBody +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.push.PushInterface +import com.reown.android.push.network.PushService +import com.reown.android.push.network.model.PushBody import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope diff --git a/core/android/src/main/kotlin/com/reown/android/push/network/PushService.kt b/core/android/src/main/kotlin/com/reown/android/push/network/PushService.kt new file mode 100644 index 000000000..f18c9b121 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/push/network/PushService.kt @@ -0,0 +1,15 @@ +package com.reown.android.push.network + +import com.reown.android.push.network.model.PushBody +import com.reown.android.push.network.model.PushResponse +import retrofit2.Response +import retrofit2.http.* + +interface PushService { + + @POST("{projectId}/clients") + suspend fun register(@Path("projectId") projectId: String, @Query("auth") clientID: String, @Body echoClientsBody: PushBody): Response + + @DELETE("{projectId}/clients/{clientId}") + suspend fun unregister(@Path("projectId") projectId: String, @Path("clientId") clientID: String): Response +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushBody.kt b/core/android/src/main/kotlin/com/reown/android/push/network/model/PushBody.kt similarity index 86% rename from core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushBody.kt rename to core/android/src/main/kotlin/com/reown/android/push/network/model/PushBody.kt index ea2e5502d..3600ce580 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushBody.kt +++ b/core/android/src/main/kotlin/com/reown/android/push/network/model/PushBody.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.push.network.model +package com.reown.android.push.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushResponse.kt b/core/android/src/main/kotlin/com/reown/android/push/network/model/PushResponse.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushResponse.kt rename to core/android/src/main/kotlin/com/reown/android/push/network/model/PushResponse.kt index 201c75ca3..1fa5328cd 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/network/model/PushResponse.kt +++ b/core/android/src/main/kotlin/com/reown/android/push/network/model/PushResponse.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.push.network.model +package com.reown.android.push.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/push/notifications/DecryptMessageUseCaseInterface.kt b/core/android/src/main/kotlin/com/reown/android/push/notifications/DecryptMessageUseCaseInterface.kt new file mode 100644 index 000000000..501cb1e77 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/push/notifications/DecryptMessageUseCaseInterface.kt @@ -0,0 +1,7 @@ +package com.reown.android.push.notifications + +import com.reown.android.Core + +interface DecryptMessageUseCaseInterface { + suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/notifications/PushMessagingService.kt b/core/android/src/main/kotlin/com/reown/android/push/notifications/PushMessagingService.kt similarity index 92% rename from core/android/src/main/kotlin/com/walletconnect/android/push/notifications/PushMessagingService.kt rename to core/android/src/main/kotlin/com/reown/android/push/notifications/PushMessagingService.kt index 34f4395f3..2160a9569 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/notifications/PushMessagingService.kt +++ b/core/android/src/main/kotlin/com/reown/android/push/notifications/PushMessagingService.kt @@ -1,13 +1,13 @@ -package com.walletconnect.android.push.notifications +package com.reown.android.push.notifications import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.internal.common.wcKoinApp +import com.reown.android.Core +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.internal.common.wcKoinApp import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import org.bouncycastle.util.encoders.Base64 diff --git a/core/android/src/main/kotlin/com/reown/android/relay/ConnectionState.kt b/core/android/src/main/kotlin/com/reown/android/relay/ConnectionState.kt new file mode 100644 index 000000000..1a7d6a3da --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/relay/ConnectionState.kt @@ -0,0 +1,12 @@ +@file:JvmSynthetic + +package com.reown.android.relay + +sealed class WSSConnectionState { + data object Connected : WSSConnectionState() + + sealed class Disconnected : WSSConnectionState() { + data class ConnectionFailed(val throwable: Throwable) : Disconnected() + data class ConnectionClosed(val message: String? = null) : Disconnected() + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/relay/ConnectionType.kt b/core/android/src/main/kotlin/com/reown/android/relay/ConnectionType.kt new file mode 100644 index 000000000..f2b285d0f --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/relay/ConnectionType.kt @@ -0,0 +1,5 @@ +package com.reown.android.relay + +enum class ConnectionType { + AUTOMATIC, MANUAL +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/relay/NetworkClientTimeout.kt b/core/android/src/main/kotlin/com/reown/android/relay/NetworkClientTimeout.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/relay/NetworkClientTimeout.kt rename to core/android/src/main/kotlin/com/reown/android/relay/NetworkClientTimeout.kt index a500ff355..e420d565c 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/relay/NetworkClientTimeout.kt +++ b/core/android/src/main/kotlin/com/reown/android/relay/NetworkClientTimeout.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.relay +package com.reown.android.relay import java.util.concurrent.TimeUnit @@ -20,7 +20,7 @@ data class NetworkClientTimeout( companion object { - private const val MIN_TIMEOUT_LIMIT_AS_MILLIS = 10_000L + private const val MIN_TIMEOUT_LIMIT_AS_MILLIS = 15_000L private const val MAX_TIMEOUT_LIMIT_AS_MILLIS = 60_000L fun getDefaultTimeout() = NetworkClientTimeout( diff --git a/core/android/src/main/kotlin/com/reown/android/relay/RelayClient.kt b/core/android/src/main/kotlin/com/reown/android/relay/RelayClient.kt new file mode 100644 index 000000000..0b521b21c --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/relay/RelayClient.kt @@ -0,0 +1,130 @@ +@file:JvmSynthetic + +package com.reown.android.relay + +import com.reown.android.Core +import com.reown.android.internal.common.connection.ConnectivityState +import com.reown.android.internal.common.connection.DefaultConnectionLifecycle +import com.reown.android.internal.common.connection.ManualConnectionLifecycle +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.exception.WRONG_CONNECTION_TYPE +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.utils.toWalletConnectException +import com.reown.foundation.network.BaseRelayClient +import com.reown.foundation.network.model.Relay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import org.koin.core.KoinApplication +import org.koin.core.qualifier.named + +class RelayClient(private val koinApp: KoinApplication = wcKoinApp) : BaseRelayClient(), RelayConnectionInterface { + private val manualConnection: ManualConnectionLifecycle by lazy { koinApp.koin.get(named(AndroidCommonDITags.MANUAL_CONNECTION_LIFECYCLE)) } + private val defaultConnection: DefaultConnectionLifecycle by lazy { koinApp.koin.get(named(AndroidCommonDITags.DEFAULT_CONNECTION_LIFECYCLE)) } + private val networkState: ConnectivityState by lazy { koinApp.koin.get(named(AndroidCommonDITags.CONNECTIVITY_STATE)) } + override val isNetworkAvailable: StateFlow by lazy { networkState.isAvailable } + private val _wssConnectionState: MutableStateFlow = MutableStateFlow(WSSConnectionState.Disconnected.ConnectionClosed()) + override val wssConnectionState: StateFlow = _wssConnectionState + private lateinit var connectionType: ConnectionType + override val onResubscribe: Flow + get() = merge( + connectionLifecycle.onResume.filter { isResumed -> isResumed != null && isResumed }, + wssConnectionState.filterIsInstance(WSSConnectionState.Connected::class) + ) + + @JvmSynthetic + fun initialize(connectionType: ConnectionType, onError: (Throwable) -> Unit) { + this.connectionType = connectionType + logger = koinApp.koin.get(named(AndroidCommonDITags.LOGGER)) + relayService = koinApp.koin.get(named(AndroidCommonDITags.RELAY_SERVICE)) + connectionLifecycle = if (connectionType == ConnectionType.MANUAL) manualConnection else defaultConnection + + collectConnectionInitializationErrors { error -> onError(error) } + monitorConnectionState() + observeResults() + } + + private fun collectConnectionInitializationErrors(onError: (Throwable) -> Unit) { + scope.launch { + supervisorScope { + eventsFlow + .first { event -> + if (event is Relay.Model.Event.OnConnectionFailed) { + onError(event.throwable.toWalletConnectException) + } + + event !is Relay.Model.Event.OnConnectionOpened<*> + } + } + } + } + + private fun monitorConnectionState() { + eventsFlow + .onEach { event: Relay.Model.Event -> setIsWSSConnectionOpened(event) } + .launchIn(scope) + } + + private fun setIsWSSConnectionOpened(event: Relay.Model.Event) { + when { + event is Relay.Model.Event.OnConnectionOpened<*> && _wssConnectionState.value is WSSConnectionState.Disconnected -> + _wssConnectionState.value = WSSConnectionState.Connected + + event is Relay.Model.Event.OnConnectionFailed && _wssConnectionState.value is WSSConnectionState.Connected -> + _wssConnectionState.value = WSSConnectionState.Disconnected.ConnectionFailed(event.throwable.toWalletConnectException) + + event is Relay.Model.Event.OnConnectionClosed && _wssConnectionState.value is WSSConnectionState.Connected -> + _wssConnectionState.value = WSSConnectionState.Disconnected.ConnectionClosed("Connection closed: ${event.shutdownReason.reason} ${event.shutdownReason.code}") + } + } + + override fun connect(onError: (Core.Model.Error) -> Unit) { + when (connectionType) { + ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) + ConnectionType.MANUAL -> manualConnection.connect() + } + } + + override fun disconnect(onError: (Core.Model.Error) -> Unit) { + when (connectionType) { + ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) + ConnectionType.MANUAL -> manualConnection.disconnect() + } + } + + override fun restart(onError: (Core.Model.Error) -> Unit) { + try { + when (connectionType) { + ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) + ConnectionType.MANUAL -> manualConnection.reconnect() + } + } catch (e: Exception) { + onError(Core.Model.Error(e)) + } + } + + @Deprecated("This has become deprecate in favor of the onError returning Core.Model.Error", replaceWith = ReplaceWith("this.connect(onErrorModel)")) + override fun connect(onErrorModel: (Core.Model.Error) -> Unit, onError: (String) -> Unit) { + when (connectionType) { + ConnectionType.AUTOMATIC -> onError(WRONG_CONNECTION_TYPE) + ConnectionType.MANUAL -> manualConnection.connect() + } + } + + @Deprecated("This has become deprecate in favor of the onError returning Core.Model.Error", replaceWith = ReplaceWith("this.disconnect(onErrorModel)")) + override fun disconnect(onErrorModel: (Core.Model.Error) -> Unit, onError: (String) -> Unit) { + when (connectionType) { + ConnectionType.AUTOMATIC -> onError(WRONG_CONNECTION_TYPE) + ConnectionType.MANUAL -> manualConnection.disconnect() + } + } +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/relay/RelayConnectionInterface.kt b/core/android/src/main/kotlin/com/reown/android/relay/RelayConnectionInterface.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/relay/RelayConnectionInterface.kt rename to core/android/src/main/kotlin/com/reown/android/relay/RelayConnectionInterface.kt index c3f13d6b9..71bd5f3ac 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/relay/RelayConnectionInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/relay/RelayConnectionInterface.kt @@ -1,12 +1,14 @@ -package com.walletconnect.android.relay +package com.reown.android.relay -import com.walletconnect.android.Core -import com.walletconnect.foundation.network.RelayInterface +import com.reown.android.Core +import com.reown.foundation.network.RelayInterface +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow interface RelayConnectionInterface : RelayInterface { val wssConnectionState: StateFlow val isNetworkAvailable: StateFlow + val onResubscribe: Flow @Deprecated("This has become deprecate in favor of the onError returning Core.Model.Error", ReplaceWith("this.connect(onErrorModel)")) fun connect(onErrorModel: (Core.Model.Error) -> Unit = {}, onError: (String) -> Unit) diff --git a/core/android/src/main/kotlin/com/reown/android/utils/Extensions.kt b/core/android/src/main/kotlin/com/reown/android/utils/Extensions.kt new file mode 100644 index 000000000..e2310ef07 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/utils/Extensions.kt @@ -0,0 +1,64 @@ +@file:JvmSynthetic + +package com.reown.android.utils + +import android.net.Uri +import com.reown.android.Core +import com.reown.android.internal.common.exception.GenericException +import com.reown.android.internal.common.exception.InvalidProjectIdException +import com.reown.android.internal.common.exception.ProjectIdDoesNotExistException +import com.reown.android.internal.common.exception.UnableToConnectToWebsocketException +import com.reown.android.internal.common.exception.WalletConnectException +import com.reown.android.internal.common.model.AppMetaData +import com.reown.utils.Empty +import java.net.HttpURLConnection + +@JvmSynthetic +internal fun String.strippedUrl() = Uri.parse(this).run { + this@run.scheme + "://" + this@run.authority +} + +@JvmSynthetic +internal fun String.isValidRelayServerUrl(): Boolean { + return this.isNotBlank() && Uri.parse(this)?.let { relayUrl -> + arrayOf("wss", "ws").contains(relayUrl.scheme) && !relayUrl.getQueryParameter("projectId").isNullOrBlank() + } ?: false +} + +// Assumes isValidRelayServerUrl returns true. +@JvmSynthetic +internal fun String.projectId(): String { + return Uri.parse(this)!!.let { relayUrl -> + relayUrl.getQueryParameter("projectId")!! + } +} + +@get:JvmSynthetic +internal val Throwable.toWalletConnectException: WalletConnectException + get() = + when { + this.message?.contains(HttpURLConnection.HTTP_UNAUTHORIZED.toString()) == true -> + UnableToConnectToWebsocketException("${this.message}. It's possible that JWT has expired. Try initializing the CoreClient again.") + + this.message?.contains(HttpURLConnection.HTTP_NOT_FOUND.toString()) == true -> + ProjectIdDoesNotExistException("Project ID doesn't exist: ${this.message}") + + this.message?.contains(HttpURLConnection.HTTP_FORBIDDEN.toString()) == true -> + InvalidProjectIdException("Invalid project ID: ${this.message}") + + else -> GenericException("Error while connecting, please check your Internet connection or contact support: ${this.message}") + } + +@get:JvmSynthetic +val Int.Companion.DefaultId + get() = -1 + +fun AppMetaData?.toClient() = Core.Model.AppMetaData( + name = this?.name ?: String.Empty, + description = this?.description ?: String.Empty, + url = this?.url ?: String.Empty, + icons = this?.icons ?: emptyList(), + redirect = this?.redirect?.native, + appLink = this?.redirect?.universal, + linkMode = this?.redirect?.linkMode ?: false +) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/utils/PackageManagerExtensions.kt b/core/android/src/main/kotlin/com/reown/android/utils/PackageManagerExtensions.kt similarity index 94% rename from core/android/src/main/kotlin/com/walletconnect/android/utils/PackageManagerExtensions.kt rename to core/android/src/main/kotlin/com/reown/android/utils/PackageManagerExtensions.kt index 0aef4116e..0d49fcef4 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/utils/PackageManagerExtensions.kt +++ b/core/android/src/main/kotlin/com/reown/android/utils/PackageManagerExtensions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.utils +package com.reown.android.utils import android.content.pm.PackageManager import android.os.Build diff --git a/core/android/src/main/kotlin/com/walletconnect/android/utils/Timber.kt b/core/android/src/main/kotlin/com/reown/android/utils/Timber.kt similarity index 89% rename from core/android/src/main/kotlin/com/walletconnect/android/utils/Timber.kt rename to core/android/src/main/kotlin/com/reown/android/utils/Timber.kt index 72cf2f907..4e14cb32b 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/utils/Timber.kt +++ b/core/android/src/main/kotlin/com/reown/android/utils/Timber.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.utils +package com.reown.android.utils -import com.walletconnect.android.BuildConfig +import com.reown.android.BuildConfig import timber.log.Timber internal fun plantTimber() { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/utils/cacao/CacaoSignerInterface.kt b/core/android/src/main/kotlin/com/reown/android/utils/cacao/CacaoSignerInterface.kt similarity index 90% rename from core/android/src/main/kotlin/com/walletconnect/android/utils/cacao/CacaoSignerInterface.kt rename to core/android/src/main/kotlin/com/reown/android/utils/cacao/CacaoSignerInterface.kt index 00958c2af..bed97f267 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/utils/cacao/CacaoSignerInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/utils/cacao/CacaoSignerInterface.kt @@ -1,14 +1,14 @@ @file:JvmName("CacaoSignerUtil") @file:Suppress("PackageDirectoryMismatch", "UNCHECKED_CAST") // Added to dismiss confusion. Cast to `T` always succeeds as Cacao.Signature implements ISignature. -package com.walletconnect.android.utils.cacao +package com.reown.android.utils.cacao -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.cacao.signature.ISignatureType -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.eip191.EIP191Signer -import com.walletconnect.android.internal.common.signing.signature.toCacaoSignature +import com.reown.android.cacao.SignatureInterface +import com.reown.android.cacao.signature.ISignatureType +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.eip191.EIP191Signer +import com.reown.android.internal.common.signing.signature.toCacaoSignature import org.web3j.utils.Numeric.hexStringToByteArray import kotlin.reflect.KFunction import kotlin.reflect.full.createType diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyClient.kt b/core/android/src/main/kotlin/com/reown/android/verify/client/VerifyClient.kt similarity index 81% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyClient.kt rename to core/android/src/main/kotlin/com/reown/android/verify/client/VerifyClient.kt index a66799206..e9201c61b 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyClient.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/client/VerifyClient.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.verify.client +package com.reown.android.verify.client -import com.walletconnect.android.internal.common.di.verifyModule -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.verify.domain.VerifyRepository -import com.walletconnect.android.verify.domain.VerifyResult +import com.reown.android.internal.common.di.verifyModule +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.verify.domain.VerifyRepository +import com.reown.android.verify.domain.VerifyResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyInterface.kt b/core/android/src/main/kotlin/com/reown/android/verify/client/VerifyInterface.kt similarity index 79% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyInterface.kt rename to core/android/src/main/kotlin/com/reown/android/verify/client/VerifyInterface.kt index b179c5d6f..7b880e3d8 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/client/VerifyInterface.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/client/VerifyInterface.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.verify.client +package com.reown.android.verify.client -import com.walletconnect.android.verify.domain.VerifyResult +import com.reown.android.verify.domain.VerifyResult interface VerifyInterface { fun initialize() diff --git a/core/android/src/main/kotlin/com/reown/android/verify/data/VerifyService.kt b/core/android/src/main/kotlin/com/reown/android/verify/data/VerifyService.kt new file mode 100644 index 000000000..63afaf132 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/verify/data/VerifyService.kt @@ -0,0 +1,25 @@ +package com.reown.android.verify.data + +import com.reown.android.verify.model.Origin +import com.reown.android.verify.model.RegisterAttestationBody +import com.reown.android.verify.model.VerifyServerPublicKey +import com.reown.android.verify.model.VerifyServerResponse +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.Headers +import retrofit2.http.POST +import retrofit2.http.Path + +interface VerifyService { + @Headers("Content-Type: application/json") + @POST("attestation") + suspend fun registerAttestation(@Body body: RegisterAttestationBody): Response + + @Headers("Content-Type: application/json") + @GET("attestation/{attestationId}?v2Supported=true") + suspend fun resolveAttestation(@Path("attestationId") attestationId: String): Response + + @GET("v2/public-key") + suspend fun getPublicKey(): Response +} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/JWTRepository.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/JWTRepository.kt similarity index 93% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/domain/JWTRepository.kt rename to core/android/src/main/kotlin/com/reown/android/verify/domain/JWTRepository.kt index 5696d0c95..c8a0af451 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/JWTRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/JWTRepository.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.verify.domain +package com.reown.android.verify.domain -import com.walletconnect.android.verify.model.JWK -import com.walletconnect.foundation.util.jwt.JWT_DELIMITER -import com.walletconnect.util.bytesToHex +import com.reown.android.verify.model.JWK +import com.reown.foundation.util.jwt.JWT_DELIMITER +import com.reown.util.bytesToHex import org.bouncycastle.crypto.digests.SHA256Digest import org.bouncycastle.crypto.params.ECDomainParameters import org.bouncycastle.crypto.params.ECPublicKeyParameters diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/ResolveAttestationIdUseCase.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/ResolveAttestationIdUseCase.kt similarity index 84% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/domain/ResolveAttestationIdUseCase.kt rename to core/android/src/main/kotlin/com/reown/android/verify/domain/ResolveAttestationIdUseCase.kt index 9ad879eb0..bc74cf9dd 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/ResolveAttestationIdUseCase.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/ResolveAttestationIdUseCase.kt @@ -1,13 +1,13 @@ -package com.walletconnect.android.verify.domain +package com.reown.android.verify.domain -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.verify.client.VerifyInterface -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.utils.Empty +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.verify.client.VerifyInterface +import com.reown.android.verify.model.VerifyContext +import com.reown.utils.Empty import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyPublicKeyStorageRepository.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyPublicKeyStorageRepository.kt similarity index 82% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyPublicKeyStorageRepository.kt rename to core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyPublicKeyStorageRepository.kt index da1456544..aeb88f0c8 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyPublicKeyStorageRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyPublicKeyStorageRepository.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.verify.domain +package com.reown.android.verify.domain import android.database.sqlite.SQLiteException -import com.walletconnect.android.sdk.storage.data.dao.VerifyPublicKeyQueries +import com.reown.android.sdk.storage.data.dao.VerifyPublicKeyQueries class VerifyPublicKeyStorageRepository(private val queries: VerifyPublicKeyQueries) { diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyRepository.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyRepository.kt similarity index 94% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyRepository.kt rename to core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyRepository.kt index 157eb82b1..509dfb4ac 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyRepository.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyRepository.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.verify.domain +package com.reown.android.verify.domain import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.verify.data.VerifyService -import com.walletconnect.android.verify.model.VerifyClaims -import com.walletconnect.util.hexToBytes +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.verify.data.VerifyService +import com.reown.android.verify.model.VerifyClaims +import com.reown.util.hexToBytes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob diff --git a/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyResult.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyResult.kt new file mode 100644 index 000000000..48df0ff35 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyResult.kt @@ -0,0 +1,9 @@ +package com.reown.android.verify.domain + +import com.reown.android.internal.common.model.Validation + +data class VerifyResult( + val validation: Validation, + val isScam: Boolean?, + val origin: String +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyUtils.kt b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyUtils.kt new file mode 100644 index 000000000..2119b226f --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/verify/domain/VerifyUtils.kt @@ -0,0 +1,6 @@ +package com.reown.android.verify.domain + +import com.reown.android.internal.common.model.Validation +import com.reown.utils.compareDomains + +fun getValidation(metadataUrl: String, origin: String) = if (compareDomains(metadataUrl, origin)) Validation.VALID else Validation.INVALID \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/Origin.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/Origin.kt similarity index 77% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/model/Origin.kt rename to core/android/src/main/kotlin/com/reown/android/verify/model/Origin.kt index 13e0ad569..d72e85f78 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/Origin.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/Origin.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.verify.model +package com.reown.android.verify.model import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/RegisterAttestationBody.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/RegisterAttestationBody.kt similarity index 76% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/model/RegisterAttestationBody.kt rename to core/android/src/main/kotlin/com/reown/android/verify/model/RegisterAttestationBody.kt index 76d9cf3dd..21f71cd99 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/RegisterAttestationBody.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/RegisterAttestationBody.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.verify.model +package com.reown.android.verify.model import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyClaims.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyClaims.kt similarity index 88% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyClaims.kt rename to core/android/src/main/kotlin/com/reown/android/verify/model/VerifyClaims.kt index be094aea4..c6fd81c9e 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyClaims.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyClaims.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.verify.model +package com.reown.android.verify.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyContext.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyContext.kt new file mode 100644 index 000000000..3392440a9 --- /dev/null +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyContext.kt @@ -0,0 +1,11 @@ +package com.reown.android.verify.model + +import com.reown.android.internal.common.model.Validation + +data class VerifyContext( + val id: Long, + val origin: String, + val validation: Validation, + val verifyUrl: String, + val isScam: Boolean? +) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerPublicKey.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerPublicKey.kt similarity index 92% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerPublicKey.kt rename to core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerPublicKey.kt index 6ad0608cb..8ee1d3c98 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerPublicKey.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerPublicKey.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.verify.model +package com.reown.android.verify.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerResponse.kt b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerResponse.kt similarity index 94% rename from core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerResponse.kt rename to core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerResponse.kt index a27f87f69..ecf03e570 100644 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyServerResponse.kt +++ b/core/android/src/main/kotlin/com/reown/android/verify/model/VerifyServerResponse.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.verify.model +package com.reown.android.verify.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/core/android/src/main/kotlin/com/walletconnect/android/CoreInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/CoreInterface.kt deleted file mode 100644 index f80658fae..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/CoreInterface.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.walletconnect.android - -import android.app.Application -import com.walletconnect.android.internal.common.explorer.ExplorerInterface -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.push.PushInterface -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.NetworkClientTimeout -import com.walletconnect.android.relay.RelayConnectionInterface -import com.walletconnect.android.verify.client.VerifyInterface - -interface CoreInterface { - val Pairing: PairingInterface - val PairingController: PairingControllerInterface - val Relay: RelayConnectionInterface - @Deprecated(message = "Replaced with Push") - val Echo: PushInterface - val Push: PushInterface - val Verify: VerifyInterface - val Explorer: ExplorerInterface - - interface Delegate : PairingInterface.Delegate - - fun setDelegate(delegate: Delegate) - - fun initialize( - metaData: Core.Model.AppMetaData, - relayServerUrl: String, - connectionType: ConnectionType = ConnectionType.AUTOMATIC, - application: Application, - relay: RelayConnectionInterface? = null, - keyServerUrl: String? = null, - networkClientTimeout: NetworkClientTimeout? = null, - telemetryEnabled: Boolean = true, - onError: (Core.Model.Error) -> Unit, - ) - - fun initialize( - application: Application, - projectId: String, - metaData: Core.Model.AppMetaData, - connectionType: ConnectionType = ConnectionType.AUTOMATIC, - relay: RelayConnectionInterface? = null, - keyServerUrl: String? = null, - networkClientTimeout: NetworkClientTimeout? = null, - telemetryEnabled: Boolean = true, - onError: (Core.Model.Error) -> Unit, - ) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/CoreProtocol.kt b/core/android/src/main/kotlin/com/walletconnect/android/CoreProtocol.kt deleted file mode 100644 index d90923d32..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/CoreProtocol.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.walletconnect.android - -import android.app.Application -import android.content.SharedPreferences -import com.walletconnect.android.di.coreStorageModule -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.di.KEY_CLIENT_ID -import com.walletconnect.android.internal.common.di.coreAndroidNetworkModule -import com.walletconnect.android.internal.common.di.coreCommonModule -import com.walletconnect.android.internal.common.di.coreCryptoModule -import com.walletconnect.android.internal.common.di.coreJsonRpcModule -import com.walletconnect.android.internal.common.di.corePairingModule -import com.walletconnect.android.internal.common.di.explorerModule -import com.walletconnect.android.internal.common.di.keyServerModule -import com.walletconnect.android.internal.common.di.pulseModule -import com.walletconnect.android.internal.common.di.pushModule -import com.walletconnect.android.internal.common.di.web3ModalModule -import com.walletconnect.android.internal.common.explorer.ExplorerInterface -import com.walletconnect.android.internal.common.explorer.ExplorerProtocol -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.client.PairingProtocol -import com.walletconnect.android.pairing.handler.PairingController -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.push.PushInterface -import com.walletconnect.android.push.client.PushClient -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.NetworkClientTimeout -import com.walletconnect.android.relay.RelayClient -import com.walletconnect.android.relay.RelayConnectionInterface -import com.walletconnect.android.utils.isValidRelayServerUrl -import com.walletconnect.android.utils.plantTimber -import com.walletconnect.android.utils.projectId -import com.walletconnect.android.verify.client.VerifyClient -import com.walletconnect.android.verify.client.VerifyInterface -import org.koin.android.ext.koin.androidContext -import org.koin.core.KoinApplication -import org.koin.core.qualifier.named -import org.koin.dsl.module - -class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInterface { - override val Pairing: PairingInterface = PairingProtocol(koinApp) - override val PairingController: PairingControllerInterface = PairingController(koinApp) - override var Relay = RelayClient(koinApp) - - @Deprecated(message = "Replaced with Push") - override val Echo: PushInterface = PushClient - override val Push: PushInterface = PushClient - override val Verify: VerifyInterface = VerifyClient(koinApp) - override val Explorer: ExplorerInterface = ExplorerProtocol(koinApp) - - init { - plantTimber() - } - - override fun setDelegate(delegate: CoreInterface.Delegate) { - Pairing.setDelegate(delegate) - } - - companion object { - val instance = CoreProtocol() - } - - override fun initialize( - metaData: Core.Model.AppMetaData, - relayServerUrl: String, - connectionType: ConnectionType, - application: Application, - relay: RelayConnectionInterface?, - keyServerUrl: String?, - networkClientTimeout: NetworkClientTimeout?, - telemetryEnabled: Boolean, - onError: (Core.Model.Error) -> Unit - ) { - try { - require(relayServerUrl.isValidRelayServerUrl()) { "Check the schema and projectId parameter of the Server Url" } - - setup( - application = application, - serverUrl = relayServerUrl, - projectId = relayServerUrl.projectId(), - telemetryEnabled = telemetryEnabled, - connectionType = connectionType, - networkClientTimeout = networkClientTimeout, - relay = relay, - onError = onError, - metaData = metaData, - keyServerUrl = keyServerUrl - ) - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - } - - override fun initialize( - application: Application, - projectId: String, - metaData: Core.Model.AppMetaData, - connectionType: ConnectionType, - relay: RelayConnectionInterface?, - keyServerUrl: String?, - networkClientTimeout: NetworkClientTimeout?, - telemetryEnabled: Boolean, - onError: (Core.Model.Error) -> Unit - ) { - try { - require(projectId.isNotEmpty()) { "Project Id cannot be empty" } - - setup( - application = application, - projectId = projectId, - telemetryEnabled = telemetryEnabled, - connectionType = connectionType, - networkClientTimeout = networkClientTimeout, - relay = relay, - onError = onError, - metaData = metaData, - keyServerUrl = keyServerUrl - ) - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - } - - private fun CoreProtocol.setup( - application: Application, - serverUrl: String? = null, - projectId: String, - telemetryEnabled: Boolean, - connectionType: ConnectionType, - networkClientTimeout: NetworkClientTimeout?, - relay: RelayConnectionInterface?, - onError: (Core.Model.Error) -> Unit, - metaData: Core.Model.AppMetaData, - keyServerUrl: String? - ) { - val bundleId: String = application.packageName - val relayServerUrl = if (serverUrl.isNullOrEmpty()) "wss://relay.walletconnect.org?projectId=$projectId" else serverUrl - - with(koinApp) { - androidContext(application) - modules( - module { single { ProjectId(projectId) } }, - module { single(named(AndroidCommonDITags.TELEMETRY_ENABLED)) { TelemetryEnabled(telemetryEnabled) } }, - coreAndroidNetworkModule(relayServerUrl, connectionType, BuildConfig.SDK_VERSION, networkClientTimeout, bundleId), - coreCommonModule(), - coreCryptoModule(), - ) - - if (relay == null) { - Relay.initialize(connectionType) { error -> onError(Core.Model.Error(error)) } - } - - modules( - coreStorageModule(bundleId = bundleId), - module { single(named(AndroidCommonDITags.CLIENT_ID)) { requireNotNull(get().getString(KEY_CLIENT_ID, null)) } }, - pushModule(), - module { single { relay ?: Relay } }, - module { - single { - with(metaData) { - AppMetaData( - name = name, - description = description, - url = url, - icons = icons, - redirect = Redirect(native = redirect, universal = appLink, linkMode = linkMode) - ) - } - } - }, - module { single { Echo } }, - module { single { Push } }, - module { single { Verify } }, - coreJsonRpcModule(), - corePairingModule(Pairing, PairingController), - keyServerModule(keyServerUrl), - explorerModule(), - web3ModalModule(), - pulseModule(bundleId) - ) - } - - Pairing.initialize() - PairingController.initialize() - Verify.initialize() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/Messages.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/Messages.kt deleted file mode 100644 index abe82265a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/Messages.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal - -@JvmSynthetic -internal const val MALFORMED_PAIRING_URI_MESSAGE: String = "Pairing URI string is invalid." - -@JvmSynthetic -internal const val PAIRING_NOT_ALLOWED_MESSAGE: String = "Pairing already exists. Please try again with a new connection URI" - -@JvmSynthetic -internal const val NO_SEQUENCE_FOR_TOPIC_MESSAGE: String = "Cannot find sequence for given topic: " \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/KoinApplication.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/KoinApplication.kt deleted file mode 100644 index c70280d9e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/KoinApplication.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.internal.common - -import org.koin.core.KoinApplication - -var wcKoinApp: KoinApplication = KoinApplication.init().apply { createEagerInstances() } \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ManualConnectionLifecycle.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ManualConnectionLifecycle.kt deleted file mode 100644 index 395a04df7..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/connection/ManualConnectionLifecycle.kt +++ /dev/null @@ -1,23 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.connection - -import com.tinder.scarlet.Lifecycle -import com.tinder.scarlet.lifecycle.LifecycleRegistry - -internal class ManualConnectionLifecycle( - private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(), -) : Lifecycle by lifecycleRegistry { - fun connect() { - lifecycleRegistry.onNext(Lifecycle.State.Started) - } - - fun disconnect() { - lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason()) - } - - fun restart() { - lifecycleRegistry.onNext(Lifecycle.State.Stopped.WithReason()) - lifecycleRegistry.onNext(Lifecycle.State.Started) - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/Utils.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/Utils.kt deleted file mode 100644 index a44083c8c..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/Utils.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.android.internal.common.crypto - -import com.walletconnect.util.bytesToHex -import java.security.MessageDigest - - -private const val SHA_256: String = "SHA-256" - -fun sha256(input: ByteArray): String { - val messageDigest: MessageDigest = MessageDigest.getInstance(SHA_256) - val hashedBytes: ByteArray = messageDigest.digest(input) - - return hashedBytes.bytesToHex() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/Codec.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/Codec.kt deleted file mode 100644 index 3b0e27f80..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/codec/Codec.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal.common.crypto.codec - -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.foundation.common.model.Topic - -interface Codec { - fun encrypt(topic: Topic, payload: String, envelopeType: EnvelopeType, participants: Participants? = null): ByteArray - fun decrypt(topic: Topic, cipherText: ByteArray): String -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/KeyManagementRepository.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/KeyManagementRepository.kt deleted file mode 100644 index c3b602f8f..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/crypto/kmr/KeyManagementRepository.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.walletconnect.android.internal.common.crypto.kmr - -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.foundation.common.model.Key -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -interface KeyManagementRepository { - fun setKey(key: Key, tag: String) - @Throws(MissingKeyException::class) - fun removeKeys(tag: String) - - fun getPublicKey(tag: String): PublicKey - fun getSymmetricKey(tag: String): SymmetricKey - fun getKeyPair(key: PublicKey): Pair - fun setKeyPair(publicKey: PublicKey, privateKey: PrivateKey) - fun generateAndStoreEd25519KeyPair(): PublicKey - - fun deriveAndStoreEd25519KeyPair(privateKey: PrivateKey): PublicKey - - fun generateAndStoreX25519KeyPair(): PublicKey - fun setKeyAgreement(topic: Topic, self: PublicKey, peer: PublicKey) - fun getSelfPublicFromKeyAgreement(topic: Topic): PublicKey - - fun generateAndStoreSymmetricKey(topic: Topic): SymmetricKey - fun generateSymmetricKeyFromKeyAgreement(self: PublicKey, peer: PublicKey): SymmetricKey - - fun getTopicFromKey(key: Key): Topic - fun generateTopicFromKeyAgreement(self: PublicKey, peer: PublicKey): Topic -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCommonModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCommonModule.kt deleted file mode 100644 index 092f4b2f0..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreCommonModule.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.walletconnect.android.internal.common.di - -import com.squareup.moshi.Moshi -import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory -import com.tinder.scarlet.utils.getRawType -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.adapter.ExpiryAdapter -import com.walletconnect.android.internal.common.adapter.JsonRpcResultAdapter -import com.walletconnect.android.internal.common.adapter.TagsAdapter -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.di.FoundationDITags -import com.walletconnect.foundation.di.foundationCommonModule -import com.walletconnect.foundation.util.Logger -import org.koin.core.qualifier.named -import org.koin.dsl.module -import timber.log.Timber -import kotlin.reflect.jvm.jvmName - -fun coreCommonModule() = module { - - includes(foundationCommonModule()) - - single> { - PolymorphicJsonAdapterFactory.of(JsonRpcResponse::class.java, "type") - .withSubtype(JsonRpcResponse.JsonRpcResult::class.java, "result") - .withSubtype(JsonRpcResponse.JsonRpcError::class.java, "error") - } - - single(named(AndroidCommonDITags.MOSHI)) { - get(named(FoundationDITags.MOSHI)) - .newBuilder() - .add { type, _, moshi -> - when (type.getRawType().name) { - Expiry::class.jvmName -> ExpiryAdapter - Tags::class.jvmName -> TagsAdapter - JsonRpcResponse.JsonRpcResult::class.jvmName -> JsonRpcResultAdapter(moshi) - else -> null - } - } - .add(get>()) - .add(get>()) - } - - single { - Timber - } - - single(named(AndroidCommonDITags.LOGGER)) { - object : Logger { - override fun log(logMsg: String?) { - get().d(logMsg) - } - - override fun log(throwable: Throwable?) { - get().d(throwable) - } - - override fun error(errorMsg: String?) { - get().e(errorMsg) - } - - override fun error(throwable: Throwable?) { - get().e(throwable) - } - } - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreJsonRpcModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreJsonRpcModule.kt deleted file mode 100644 index 15ad468fe..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CoreJsonRpcModule.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.walletconnect.android.internal.common.di - -import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractor -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.json_rpc.domain.relay.RelayJsonRpcInteractor -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.type.SerializableJsonRpc -import com.walletconnect.android.pairing.model.PairingJsonRpcMethod -import com.walletconnect.android.pairing.model.PairingRpc -import com.walletconnect.utils.JsonAdapterEntry -import com.walletconnect.utils.addDeserializerEntry -import com.walletconnect.utils.addSerializerEntry -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.dsl.module -import kotlin.reflect.KClass - -@JvmSynthetic -fun coreJsonRpcModule() = module { - - single { - RelayJsonRpcInteractor( - relay = get(), - chaChaPolyCodec = get(), - jsonRpcHistory = get(), - pushMessageStorage = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - ) - } - - addSerializerEntry(PairingRpc.PairingPing::class) - addSerializerEntry(PairingRpc.PairingDelete::class) - - addDeserializerEntry(PairingJsonRpcMethod.WC_PAIRING_PING, PairingRpc.PairingPing::class) - addDeserializerEntry(PairingJsonRpcMethod.WC_PAIRING_DELETE, PairingRpc.PairingDelete::class) - - factory { - JsonRpcSerializer( - serializerEntries = getAll>().toSet(), - deserializerEntries = getAll>>().toMap(), - jsonAdapterEntries = getAll>().toSet(), - moshiBuilder = get(named(AndroidCommonDITags.MOSHI)) - ) - } - - single { - LinkModeJsonRpcInteractor( - chaChaPolyCodec = get(), - jsonRpcHistory = get(), - context = androidContext() - ) - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CorePairingModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CorePairingModule.kt deleted file mode 100644 index 0c3fad1d6..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/CorePairingModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.android.internal.common.di - -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.engine.domain.PairingEngine -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import org.koin.core.qualifier.named -import org.koin.dsl.module - -fun corePairingModule(pairing: PairingInterface, pairingController: PairingControllerInterface) = module { - single { - PairingEngine( - selfMetaData = get(), - crypto = get(), - metadataRepository = get(), - pairingRepository = get(), - jsonRpcInteractor = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - insertEventUseCase = get(), - sendBatchEventUseCase = get(), - ) - } - single { pairing } - single { pairingController } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/ExplorerModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/ExplorerModule.kt deleted file mode 100644 index 274ed912c..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/ExplorerModule.kt +++ /dev/null @@ -1,38 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.di - -import com.walletconnect.android.internal.common.explorer.ExplorerRepository -import com.walletconnect.android.internal.common.explorer.data.network.ExplorerService -import com.walletconnect.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase -import com.walletconnect.android.internal.common.explorer.domain.usecase.GetProjectsWithPaginationUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -@JvmSynthetic -internal fun explorerModule() = module { - - single(named(AndroidCommonDITags.EXPLORER_URL)) { "https://registry.walletconnect.org/" } - - single(named(AndroidCommonDITags.EXPLORER_RETROFIT)) { - Retrofit.Builder() - .baseUrl(get(named(AndroidCommonDITags.EXPLORER_URL))) - .client(get(named(AndroidCommonDITags.OK_HTTP))) - .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) - .build() - } - - single { get(named(AndroidCommonDITags.EXPLORER_RETROFIT)).create(ExplorerService::class.java) } - - single { - ExplorerRepository( - explorerService = get(), - projectId = get(), - ) - } - - single { GetProjectsWithPaginationUseCase(get()) } - single { GetNotifyConfigUseCase(get()) } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PulseModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PulseModule.kt deleted file mode 100644 index 0528c22a3..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/PulseModule.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.walletconnect.android.internal.common.di - -import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.pulse.data.PulseService -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.domain.SendBatchEventUseCase -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.domain.SendEventUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -@JvmSynthetic -fun pulseModule(bundleId: String) = module { - single(named(AndroidCommonDITags.PULSE_URL)) { "https://pulse.walletconnect.org" } - - single(named(AndroidCommonDITags.PULSE_RETROFIT)) { - Retrofit.Builder() - .baseUrl(get(named(AndroidCommonDITags.PULSE_URL))) - .client(get(named(AndroidCommonDITags.WEB3MODAL_OKHTTP))) - .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)).build())) - .build() - } - - single { - get(named(AndroidCommonDITags.PULSE_RETROFIT)).create(PulseService::class.java) - } - - single { - SendEventUseCase( - pulseService = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - bundleId = bundleId - ) - } - - single { - SendBatchEventUseCase( - pulseService = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - telemetryEnabled = get(named(AndroidCommonDITags.TELEMETRY_ENABLED)), - eventsRepository = get(), - ) - } - - single { - InsertTelemetryEventUseCase( - logger = get(named(AndroidCommonDITags.LOGGER)), - eventsRepository = get(), - ) - } - - single { - InsertEventUseCase( - logger = get(named(AndroidCommonDITags.LOGGER)), - eventsRepository = get(), - ) - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/Web3ModalModule.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/Web3ModalModule.kt deleted file mode 100644 index e72bd5567..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/di/Web3ModalModule.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.walletconnect.android.internal.common.di - -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.internal.common.modal.Web3ModalApiRepository -import com.walletconnect.android.internal.common.modal.data.network.Web3ModalService -import com.walletconnect.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCase -import com.walletconnect.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCase -import com.walletconnect.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCase -import com.walletconnect.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetWalletsUseCase -import com.walletconnect.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface -import com.walletconnect.android.internal.common.model.ProjectId -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -@JvmSynthetic -internal fun web3ModalModule() = module { - single(named(AndroidCommonDITags.WEB3MODAL_URL)) { "https://api.web3modal.com/" } - - single(named(AndroidCommonDITags.WEB3MODAL_INTERCEPTOR)) { - Interceptor { chain -> - val updatedRequest = chain.request().newBuilder() - .addHeader("x-project-id", get().value) - .addHeader("x-sdk-version", "kotlin-${BuildConfig.SDK_VERSION}") - .build() - chain.proceed(updatedRequest) - } - } - - single(named(AndroidCommonDITags.WEB3MODAL_OKHTTP)) { - get(named(AndroidCommonDITags.OK_HTTP)) - .newBuilder() - .addInterceptor(get(named(AndroidCommonDITags.WEB3MODAL_INTERCEPTOR))) - .build() - } - - single(named(AndroidCommonDITags.WEB3MODAL_RETROFIT)) { - Retrofit.Builder() - .baseUrl(get(named(AndroidCommonDITags.WEB3MODAL_URL))) - .client(get(named(AndroidCommonDITags.WEB3MODAL_OKHTTP))) - .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) - .build() - } - - single { get(named(AndroidCommonDITags.WEB3MODAL_RETROFIT)).create(Web3ModalService::class.java) } - - single { - Web3ModalApiRepository( - web3ModalApiUrl = get(named(AndroidCommonDITags.WEB3MODAL_URL)), - web3ModalService = get(), - context = androidContext() - ) - } - - single { GetInstalledWalletsIdsUseCase(web3ModalApiRepository = get()) } - single { GetWalletsUseCase(web3ModalApiRepository = get()) } - single { GetSampleWalletsUseCase(context = get()) } - single { EnableAnalyticsUseCase(repository = get()) } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/Messages.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/Messages.kt deleted file mode 100644 index 2679342f3..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/Messages.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.android.internal.common.exception - -const val WRONG_CONNECTION_TYPE: String = "Wrong connection type. Please, choose manual connection on initialisation." -internal const val DISCONNECT_MESSAGE: String = "User disconnected" \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/PeerError.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/PeerError.kt deleted file mode 100644 index a220736e8..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/exception/PeerError.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.android.internal.common.exception - -import com.walletconnect.android.internal.common.model.type.Error - -sealed class Uncategorized : Error { - - data class NoMatchingTopic(val sequence: String, val topic: String) : Uncategorized() { - override val message: String = "No matching $sequence with topic: $topic" - override val code: Int = 1301 - } - - data class GenericError(val error: String) : Uncategorized() { - override val message: String = "Generic error: $error" - override val code: Int = 1302 - } -} - -sealed class Invalid : Error { - - data class MethodUnsupported(val method: String) : Invalid() { - override val message: String = "Unsupported Method Requested: $method" - override val code: Int = 10001 - } - - object RequestExpired: Invalid() { - override val message: String = "Request expired" - override val code: Int = 8000 - } -} - -sealed class Reason : Error { - - object UserDisconnected : Reason() { - override val message: String = DISCONNECT_MESSAGE - override val code: Int = 6000 - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerInterface.kt deleted file mode 100644 index f04b78199..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerInterface.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.explorer - -import com.walletconnect.android.internal.common.explorer.data.model.Project - -interface ExplorerInterface { - suspend fun getProjects(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerProtocol.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerProtocol.kt deleted file mode 100644 index e94413a63..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerProtocol.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.walletconnect.android.internal.common.explorer - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.explorer.data.model.Project -import com.walletconnect.android.internal.common.explorer.domain.usecase.GetProjectsWithPaginationUseCase -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.foundation.util.Logger -import org.koin.core.KoinApplication -import org.koin.core.qualifier.named - - -//discuss: Opening more endpoints to SDK consumers -class ExplorerProtocol( - private val koinApp: KoinApplication = wcKoinApp, -) : ExplorerInterface { - private val getProjectsWithPaginationUseCase: GetProjectsWithPaginationUseCase by lazy { koinApp.koin.get() } - private val logger: Logger by lazy { koinApp.koin.get(named(AndroidCommonDITags.LOGGER)) } - - override suspend fun getProjects(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> = getProjectsWithPaginationUseCase(page, entries, isVerified, isFeatured) -} - - diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerRepository.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerRepository.kt deleted file mode 100644 index 9ddf1430d..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/ExplorerRepository.kt +++ /dev/null @@ -1,175 +0,0 @@ -package com.walletconnect.android.internal.common.explorer - -import androidx.core.net.toUri -import com.walletconnect.android.internal.common.explorer.data.model.App -import com.walletconnect.android.internal.common.explorer.data.model.Colors -import com.walletconnect.android.internal.common.explorer.data.model.DappListings -import com.walletconnect.android.internal.common.explorer.data.model.Desktop -import com.walletconnect.android.internal.common.explorer.data.model.ImageUrl -import com.walletconnect.android.internal.common.explorer.data.model.Injected -import com.walletconnect.android.internal.common.explorer.data.model.Listing -import com.walletconnect.android.internal.common.explorer.data.model.Metadata -import com.walletconnect.android.internal.common.explorer.data.model.Mobile -import com.walletconnect.android.internal.common.explorer.data.model.NotificationType -import com.walletconnect.android.internal.common.explorer.data.model.NotifyConfig -import com.walletconnect.android.internal.common.explorer.data.model.Project -import com.walletconnect.android.internal.common.explorer.data.model.ProjectListing -import com.walletconnect.android.internal.common.explorer.data.model.SupportedStandard -import com.walletconnect.android.internal.common.explorer.data.network.ExplorerService -import com.walletconnect.android.internal.common.explorer.data.network.model.AppDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ColorsDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.DappListingsDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.DesktopDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ImageUrlDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.InjectedDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ListingDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.MetadataDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.MobileDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.NotificationTypeDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.NotifyConfigDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ProjectDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ProjectListingDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.SupportedStandardDTO -import com.walletconnect.android.internal.common.model.ProjectId - -//discuss: Repository could be inside domain -class ExplorerRepository( - private val explorerService: ExplorerService, - private val projectId: ProjectId, -) { - - suspend fun getAllDapps(): DappListings { - return with(explorerService.getAllDapps(projectId.value)) { - if (isSuccessful && body() != null) { - body()!!.toDappListing() - } else { - throw Throwable(errorBody()?.string()) - } - } - } - - suspend fun getProjects( - page: Int, - entries: Int, - isVerified: Boolean, - isFeatured: Boolean, - ): ProjectListing { - return with(explorerService.getProjects(projectId.value, entries, page, isVerified, isFeatured)) { - if (isSuccessful && body() != null) { - body()!!.toProjectListing() - } else { - throw Throwable(errorBody()?.string()) - } - } - } - - suspend fun getNotifyConfig( - appDomain: String, - ): NotifyConfig { - return with(explorerService.getNotifyConfig(projectId.value, appDomain)) { - if (isSuccessful && body() != null) { - body()!!.toNotifyConfig() - } else { - throw Throwable(errorBody()?.string()) - } - } - } - - private fun NotifyConfigDTO.toNotifyConfig(): NotifyConfig { - return with(data) { - NotifyConfig( - types = notificationTypes.map { it.toNotificationType() }, - name = name, - description = description, - imageUrl = imageUrl?.toImageUrl(), - homepage = homepage ?: "", - dappUrl = dappUrl, - isVerified = isVerified, - ) - } - } - - private fun NotificationTypeDTO.toNotificationType(): NotificationType = NotificationType(name = name, id = id, description = description, imageUrl = imageUrl?.toImageUrl()) - - - private fun ProjectListingDTO.toProjectListing(): ProjectListing { - return ProjectListing( - projects = projects.values.map { it.toProject() }, - count = count, - ) - } - - private fun ProjectDTO.toProject(): Project = Project( - id = id, - name = name?.takeIf { it.isNotBlank() } ?: "Name not provided", - description = description?.takeIf { it.isNotBlank() } ?: "Description not provided", - homepage = homepage?.takeIf { it.isNotBlank() } ?: "Homepage not provided", - imageId = imageId?.takeIf { it.isNotBlank() } ?: "ImageID not provided", - imageUrl = imageUrl?.toImageUrl() ?: ImageUrl("", "", ""), - dappUrl = dappUrl?.takeIf { it.isNotBlank() } ?: "Dapp url not provided", - order = order, - ) - - private fun DappListingsDTO.toDappListing(): DappListings { - return DappListings( - listings = listings.values.map { it.toListing() }, count = count, total = total - ) - } - - private fun ListingDTO.toListing(): Listing = Listing( - id = id, - name = name, - description = description, - homepage = homepage.toUri(), - chains = chains, - versions = versions, - sdks = sdks, - appType = appType, - imageId = imageId, - imageUrl = imageUrl.toImageUrl(), - app = app.toApp(), - injected = injected?.map { it.toInjected() }, - mobile = mobile.toMobile(), - desktop = desktop.toDesktop(), - supportedStandards = supportedStandards.map { it.toSupportedStandard() }, - metadata = metadata.toMetadata(), - updatedAt = updatedAt - ) - - private fun ImageUrlDTO.toImageUrl(): ImageUrl = ImageUrl( - sm = sm, - md = md, - lg = lg, - ) - - private fun AppDTO.toApp(): App = App( - browser = browser, ios = ios, android = android, mac = mac, windows = windows, linux = linux, chrome = chrome, firefox = firefox, safari = safari, edge = edge, opera = opera - ) - - private fun InjectedDTO.toInjected(): Injected = Injected( - namespace = namespace, injectedId = injectedId - ) - - private fun MobileDTO.toMobile(): Mobile = Mobile( - native = native, - universal = universal, - ) - - private fun DesktopDTO.toDesktop(): Desktop = Desktop( - native = native, - universal = universal, - ) - - private fun SupportedStandardDTO.toSupportedStandard(): SupportedStandard = SupportedStandard( - id = id, url = url, title = title, standardId = standardId, standardPrefix = standardPrefix - ) - - private fun MetadataDTO.toMetadata(): Metadata = Metadata( - shortName = shortName, - colors = colors.toColors(), - ) - - private fun ColorsDTO.toColors(): Colors = Colors( - primary = primary, secondary = secondary - ) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Colors.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Colors.kt deleted file mode 100644 index 649dcfdbd..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Colors.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class Colors( - val primary: String?, - val secondary: String? -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/DappListings.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/DappListings.kt deleted file mode 100644 index d57f7a498..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/DappListings.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class DappListings( - val listings: List, - val count: Int, - val total: Int -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Desktop.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Desktop.kt deleted file mode 100644 index 378cddc60..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Desktop.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class Desktop( - val native: String, - val universal: String? -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ImageUrl.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ImageUrl.kt deleted file mode 100644 index a33236be7..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ImageUrl.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class ImageUrl( - val sm: String, - val md: String, - val lg: String -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Injected.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Injected.kt deleted file mode 100644 index 2cf581116..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Injected.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class Injected( - val namespace: String, - val injectedId: String -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Metadata.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Metadata.kt deleted file mode 100644 index 69b13ece2..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Metadata.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class Metadata( - val shortName: String?, - val colors: Colors -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Mobile.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Mobile.kt deleted file mode 100644 index 29215c5ff..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/Mobile.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class Mobile( - val native: String?, - val universal: String? -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ProjectListing.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ProjectListing.kt deleted file mode 100644 index 8e9041bf6..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/ProjectListing.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class ProjectListing( - val projects: List, - val count: Int, -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/SupportedStandard.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/SupportedStandard.kt deleted file mode 100644 index 0ad5058f2..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/model/SupportedStandard.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.model - -data class SupportedStandard( - val id: String, - val url: String, - val title: String, - val standardId: Int, - val standardPrefix: String -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/ExplorerService.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/ExplorerService.kt deleted file mode 100644 index a14f4466d..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/ExplorerService.kt +++ /dev/null @@ -1,42 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.explorer.data.network - -import com.walletconnect.android.internal.common.explorer.data.network.model.DappListingsDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.NotifyConfigDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.ProjectListingDTO -import com.walletconnect.android.internal.common.explorer.data.network.model.WalletListingDTO -import retrofit2.Response -import retrofit2.http.GET -import retrofit2.http.Query - -interface ExplorerService { - - @GET("v3/dapps") - suspend fun getAllDapps(@Query("projectId") projectId: String): Response - - @GET("w3i/v1/projects") - suspend fun getProjects( - @Query("projectId") projectId: String, - @Query("entries") entries: Int, - @Query("page") page: Int, - @Query("isVerified") isVerified: Boolean, - @Query("isFeatured") isFeatured: Boolean, - ): Response - - @GET("w3i/v1/notify-config") - suspend fun getNotifyConfig( - @Query("projectId") projectId: String, - @Query("appDomain") appDomain: String, - ): Response - - @GET("w3m/v1/getAndroidListings") - suspend fun getAndroidWallets( - @Query("projectId") projectId: String, - @Query("chains") chains: String?, - @Query("sdkType") sdkType: String, - @Query("sdkVersion") sdkVersion: String, - @Query("excludedIds") excludedIds: String?, - @Query("recommendedIds") recommendedIds: String?, - ): Response -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletDTO.kt deleted file mode 100644 index a41105b7f..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletDTO.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class WalletDTO( - @Json(name = "id") - val id: String, - @Json(name = "name") - val name: String, - @Json(name = "description") - val description: String?, - @Json(name = "homepage") - val homePage: String, - @Json(name = "image_id") - val imageId: String, - @Json(name = "mobile") - val mobile: MobileDTO, - @Json(name = "app") - val app: AppDTO, -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletListingDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletListingDTO.kt deleted file mode 100644 index 3ddfcc769..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/WalletListingDTO.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model - -import com.squareup.moshi.Json - -data class WalletListingDTO( - @Json(name = "listings") - val listings: Map, - @Json(name = "count") - val count: Int, - @Json(name = "total") - val total: Int -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt deleted file mode 100644 index e5bd7a639..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetNotifyConfigUseCase.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.domain.usecase - -import com.walletconnect.android.internal.common.explorer.ExplorerRepository -import com.walletconnect.android.internal.common.explorer.data.model.NotifyConfig - -class GetNotifyConfigUseCase(private val explorerRepository: ExplorerRepository) { - suspend operator fun invoke(appDomain: String): Result = runCatching { explorerRepository.getNotifyConfig(appDomain) } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt deleted file mode 100644 index d8c0f3369..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/explorer/domain/usecase/GetProjectsWithPaginationUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.android.internal.common.explorer.domain.usecase - -import com.walletconnect.android.internal.common.explorer.ExplorerRepository -import com.walletconnect.android.internal.common.explorer.data.model.Project - -class GetProjectsWithPaginationUseCase( - private val explorerRepository: ExplorerRepository, -) { - suspend operator fun invoke(page: Int, entries: Int, isVerified: Boolean, isFeatured: Boolean): Result> = - runCatching { explorerRepository.getProjects(page, entries, isVerified, isFeatured).projects.sortedBy { it.order ?: Long.MAX_VALUE } } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt deleted file mode 100644 index 4ac29b721..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcHistoryRecord.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.internal.common.json_rpc.model - -import com.walletconnect.android.internal.common.model.TransportType - -data class JsonRpcHistoryRecord( - val id: Long, - val topic: String, - val method: String, - val body: String, - val response: String?, - val transportType: TransportType? -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcMapper.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcMapper.kt deleted file mode 100644 index 4c1098e66..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/json_rpc/model/JsonRpcMapper.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.json_rpc.model - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.json_rpc.domain.relay.Subscription -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.network.model.Relay - -@JvmSynthetic -internal fun JsonRpcHistoryRecord.toWCResponse(result: JsonRpcResponse, params: ClientParams): WCResponse = - WCResponse(Topic(topic), method, result, params) - -@JvmSynthetic -internal fun IrnParams.toRelay(): Relay.Model.IrnParams = - Relay.Model.IrnParams(tag.id, ttl.seconds, prompt) - -internal fun Subscription.toWCRequest(clientJsonRpc: ClientJsonRpc, params: ClientParams, transportType: TransportType): WCRequest = - WCRequest(topic, clientJsonRpc.id, clientJsonRpc.method, params, decryptedMessage, publishedAt, encryptedMessage, attestation, transportType) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt deleted file mode 100644 index 99c8d1c4e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/ClientIdJwtRepositoryAndroid.kt +++ /dev/null @@ -1,30 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.jwt.clientid - -import com.walletconnect.android.internal.common.exception.CannotFindKeyPairException -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.crypto.data.repository.BaseClientIdJwtRepository - -internal class ClientIdJwtRepositoryAndroid(private val keyChain: KeyStore) : BaseClientIdJwtRepository() { - - override fun setKeyPair(key: String, privateKey: PrivateKey, publicKey: PublicKey) { - keyChain.setKeys(CLIENT_ID_KEYPAIR_TAG, privateKey, publicKey) - } - - override fun getKeyPair(): Pair { - return if (doesKeyPairExist()) { - val (privateKey, publicKey) = keyChain.getKeys(CLIENT_ID_KEYPAIR_TAG) - ?: throw CannotFindKeyPairException("No key pair for given tag: $CLIENT_ID_KEYPAIR_TAG") - publicKey to privateKey - } else { - generateAndStoreClientIdKeyPair() - } - } - - private fun doesKeyPairExist(): Boolean { - return keyChain.checkKeys(CLIENT_ID_KEYPAIR_TAG) - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt deleted file mode 100644 index 9737692b5..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/clientid/GenerateJwtStoreClientIdUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.jwt.clientid - -import android.content.SharedPreferences -import androidx.core.content.edit -import com.walletconnect.android.internal.common.di.KEY_CLIENT_ID -import com.walletconnect.android.utils.strippedUrl -import com.walletconnect.foundation.crypto.data.repository.ClientIdJwtRepository - -internal class GenerateJwtStoreClientIdUseCase(private val clientIdJwtRepository: ClientIdJwtRepository, private val sharedPreferences: SharedPreferences) { - - operator fun invoke(relayUrl: String): String = - clientIdJwtRepository.generateJWT(relayUrl.strippedUrl()) { clientId -> - sharedPreferences.edit { - putString(KEY_CLIENT_ID, clientId) - } - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt deleted file mode 100644 index a723cff17..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/jwt/did/EncodeDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.jwt.did - -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.JwtClaims -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey -import com.walletconnect.foundation.util.jwt.jwtIatAndExp -import java.util.concurrent.TimeUnit - -interface EncodeDidJwtPayloadUseCase { - - operator fun invoke(params: Params): R - - data class Params(val identityPublicKey: PublicKey, val keyserverUrl: String, val expirySourceDuration: Long = 30, val expiryTimeUnit: TimeUnit = TimeUnit.DAYS) { - private val iatAndExp = jwtIatAndExp(timeunit = TimeUnit.SECONDS, expirySourceDuration = expirySourceDuration, expiryTimeUnit = expiryTimeUnit) - - val issuedAt: Long - get() = iatAndExp.first - - val expiration: Long - get() = iatAndExp.second - - val issuer: String - get() = encodeEd25519DidKey(identityPublicKey.keyAsBytes) - } -} - diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/Web3ModalApiRepository.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/Web3ModalApiRepository.kt deleted file mode 100644 index bbcf2d7aa..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/Web3ModalApiRepository.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.walletconnect.android.internal.common.modal - -import android.content.Context -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.modal.data.model.WalletAppData -import com.walletconnect.android.internal.common.modal.data.model.WalletListing -import com.walletconnect.android.internal.common.modal.data.network.Web3ModalService -import com.walletconnect.android.internal.common.modal.data.network.model.WalletDTO -import com.walletconnect.android.internal.common.modal.data.network.model.WalletDataDTO -import com.walletconnect.android.utils.isWalletInstalled - -internal class Web3ModalApiRepository( - private val context: Context, - private val web3ModalApiUrl: String, - private val web3ModalService: Web3ModalService -) { - - suspend fun getAndroidWalletsData(sdkType: String) = runCatching { - web3ModalService.getAndroidData(sdkType = sdkType) - }.mapCatching { response -> - response.body()!!.data.toWalletsAppData().filter { it.isInstalled } - } - - suspend fun getAnalyticsConfig(sdkType: String = "w3m") = runCatching { - web3ModalService.getAnalyticsConfig(sdkType = sdkType) - }.mapCatching { response -> - response.body()!!.isAnalyticsEnabled - } - - suspend fun getWallets( - sdkType: String, - page: Int, - search: String? = null, - excludeIds: List? = null, - includeWallets: List? = null - ) = runCatching { - web3ModalService.getWallets( - sdkType = sdkType, - page = page, - search = search, - exclude = excludeIds?.joinToString(","), - include = includeWallets?.joinToString(",") - ) - }.mapCatching { response -> - val body = response.body()!! - WalletListing( - page = page, - totalCount = body.count, - wallets = body.data.toWallets() - ) - } - - private fun List.toWallets(): List = map { walletDTO -> - Wallet( - id = walletDTO.id, - name = walletDTO.name, - homePage = walletDTO.homePage, - imageUrl = web3ModalApiUrl + "getWalletImage/${walletDTO.imageId}", - order = walletDTO.order, - mobileLink = walletDTO.mobileLink, - playStore = walletDTO.playStore, - webAppLink = walletDTO.webappLink, - linkMode = walletDTO.linkMode - ).apply { - isWalletInstalled = context.packageManager.isWalletInstalled(appPackage) - } - } - - private fun List.toWalletsAppData() = map { data -> - WalletAppData( - id = data.id, - appPackage = data.appId, - isInstalled = context.packageManager.isWalletInstalled(data.appId) - ) - } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/Wallet.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/Wallet.kt deleted file mode 100644 index 9348d8d40..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/Wallet.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.model - -import android.net.Uri - -data class Wallet( - val id: String, - val name: String, - val homePage: String, - val imageUrl: String, - val order: String, - val mobileLink: String?, - val playStore: String?, - val webAppLink: String?, - val linkMode: String?, - val isRecommended: Boolean = false -) { - val appPackage: String? = playStore?.extractPackage() - var isRecent: Boolean = false - var isWalletInstalled: Boolean = false - - val hasMobileWallet: Boolean - get() = mobileLink != null - - val hasWebApp: Boolean - get() = webAppLink != null -} - -private fun String.extractPackage(): String? = Uri.parse(this).getQueryParameter("id") diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletAppData.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletAppData.kt deleted file mode 100644 index 525729aa3..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletAppData.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.model - -data class WalletAppData( - val id: String, - val appPackage: String?, - val isInstalled: Boolean -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletListing.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletListing.kt deleted file mode 100644 index 0f999a408..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/model/WalletListing.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.model - -data class WalletListing( - val page: Int, - val totalCount: Int, - val wallets: List -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/Web3ModalService.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/Web3ModalService.kt deleted file mode 100644 index 4a60589ad..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/Web3ModalService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network - -import com.walletconnect.android.internal.common.modal.data.network.model.EnableAnalyticsDTO -import com.walletconnect.android.internal.common.modal.data.network.model.GetAndroidDataDTO -import com.walletconnect.android.internal.common.modal.data.network.model.GetWalletsDTO -import retrofit2.Response -import retrofit2.http.GET -import retrofit2.http.Header -import retrofit2.http.Query - -internal interface Web3ModalService { - @GET("getWallets") - suspend fun getWallets( - @Header("x-sdk-type") sdkType: String, - @Query("page") page: Int, - @Query("search") search: String? = null, - @Query("exclude") exclude: String? = null, - @Query("include") include: String? = null, - @Query("entries") entries: Int = 100, - @Query("platform") platform: String = "android" - ): Response - - @GET("getAndroidData") - suspend fun getAndroidData( - @Header("x-sdk-type") sdkType: String, - ): Response - - @GET("getAnalyticsConfig") - suspend fun getAnalyticsConfig( - @Header("x-sdk-type") sdkType: String, - ): Response -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt deleted file mode 100644 index ccb7dfe0b..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/EnableAnalyticsDTO.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network.model - -import com.squareup.moshi.Json - -data class EnableAnalyticsDTO( - @Json(name = "isAnalyticsEnabled") - val isAnalyticsEnabled: Boolean -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt deleted file mode 100644 index dba03eb3b..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetAndroidDataDTO.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network.model - -import com.squareup.moshi.Json - -internal class GetAndroidDataDTO( - @Json(name = "count") - val count: Int, - @Json(name = "data") - val data: List, -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetWalletsDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetWalletsDTO.kt deleted file mode 100644 index e24428afe..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/GetWalletsDTO.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network.model - -import com.squareup.moshi.Json -import com.walletconnect.android.internal.common.modal.data.network.model.WalletDTO - -internal data class GetWalletsDTO( - @Json(name = "count") - val count: Int, - @Json(name = "data") - val data: List, -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDTO.kt deleted file mode 100644 index a9b846684..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDTO.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -internal data class WalletDTO( - @Json(name = "id") - val id: String, - @Json(name = "name") - val name: String, - @Json(name = "homepage") - val homePage: String, - @Json(name = "image_id") - val imageId: String, - @Json(name = "order") - val order: String, - @Json(name = "mobile_link") - val mobileLink: String?, - @Json(name = "desktop_link") - val desktopLink: String?, - @Json(name = "webapp_link") - val webappLink: String?, - @Json(name = "app_store") - val appStore: String?, - @Json(name = "play_store") - val playStore: String?, - @Json(name = "link_mode") - val linkMode: String?, -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDataDTO.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDataDTO.kt deleted file mode 100644 index 8b61c7e89..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/data/network/model/WalletDataDTO.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal.common.modal.data.network.model - -import com.squareup.moshi.Json - -class WalletDataDTO( - @Json(name = "id") - val id: String, - @Json(name = "android_app_id") - val appId: String?, -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt deleted file mode 100644 index 716caa09b..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/EnableAnalyticsUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.android.internal.common.modal.domain.usecase - -import com.walletconnect.android.internal.common.modal.Web3ModalApiRepository -import kotlinx.coroutines.runBlocking - -interface EnableAnalyticsUseCaseInterface { - fun fetchAnalyticsConfig(): Boolean -} - -internal class EnableAnalyticsUseCase(private val repository: Web3ModalApiRepository) : EnableAnalyticsUseCaseInterface { - override fun fetchAnalyticsConfig(): Boolean { - return runBlocking { - try { - val response = repository.getAnalyticsConfig() - if (response.isSuccess) { - response.getOrDefault(false) - } else { - false - } - } catch (e: Exception) { - false - } - } - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt deleted file mode 100644 index 4bf11ae90..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetAndroidDataUseCaseInterface.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.android.internal.common.modal.domain.usecase - -import com.walletconnect.android.internal.common.modal.Web3ModalApiRepository - -interface GetInstalledWalletsIdsUseCaseInterface { - suspend operator fun invoke( - sdkType: String - ): List -} - -internal class GetInstalledWalletsIdsUseCase( - private val web3ModalApiRepository: Web3ModalApiRepository -) : GetInstalledWalletsIdsUseCaseInterface { - override suspend fun invoke(sdkType: String): List = web3ModalApiRepository.getAndroidWalletsData(sdkType).map { it.map { walletAppData -> walletAppData.id } }.getOrThrow() -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt deleted file mode 100644 index bfb73d175..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/modal/domain/usecase/GetWalletsUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.android.internal.common.modal.domain.usecase - -import com.walletconnect.android.internal.common.modal.Web3ModalApiRepository -import com.walletconnect.android.internal.common.modal.data.model.WalletListing - -interface GetWalletsUseCaseInterface { - suspend operator fun invoke( - sdkType: String, - page: Int, - search: String? = null, - excludeIds: List? = null, - includes: List? = null - ): WalletListing -} - -internal class GetWalletsUseCase( - private val web3ModalApiRepository: Web3ModalApiRepository -) : GetWalletsUseCaseInterface { - override suspend fun invoke( - sdkType: String, - page: Int, - search: String?, - excludeIds: List?, - includes: List? - ): WalletListing = web3ModalApiRepository.getWallets(sdkType, page, search, excludeIds, includes).getOrThrow() -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AccountId.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AccountId.kt deleted file mode 100644 index 1e88e2f14..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AccountId.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.utils.CoreValidator - - -@JvmInline -value class AccountId(val value: String) { - fun isValid(): Boolean = CoreValidator.isAccountIdCAIP10Compliant(value) - fun address() = value.split(":").last() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaDataType.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaDataType.kt deleted file mode 100644 index 315276940..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/AppMetaDataType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.internal.common.model - -enum class AppMetaDataType { - SELF, PEER -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ConnectionState.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ConnectionState.kt deleted file mode 100644 index 54815a640..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ConnectionState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -data class ConnectionState(val isAvailable: Boolean, val reason: Reason? = null) : EngineEvent { - sealed class Reason { - data class ConnectionClosed(val message: String) : Reason() - data class ConnectionFailed(val throwable: Throwable) : Reason() - } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/CryptoException.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/CryptoException.kt deleted file mode 100644 index d1a253259..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/CryptoException.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.common.exception.WalletConnectException - -class UnknownEnvelopeTypeException(override val message: String?) : WalletConnectException(message) -class MissingParticipantsException(override val message: String?) : WalletConnectException(message) -class MissingKeyException(override val message: String?) : WalletConnectException(message) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/DidJwt.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/DidJwt.kt deleted file mode 100644 index 08cf1fd94..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/DidJwt.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.android.internal.common.model - -@JvmInline -value class DidJwt(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/EnvelopeType.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/EnvelopeType.kt deleted file mode 100644 index bbf3fdafa..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/EnvelopeType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.internal.common.model - -enum class EnvelopeType(val id: Byte) { - ZERO(0), ONE(1), TWO(2) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Expiry.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Expiry.kt deleted file mode 100644 index 9f8b0b905..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Expiry.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.android.internal.common.model - -data class Expiry(val seconds: Long) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/IrnParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/IrnParams.kt deleted file mode 100644 index 30f508615..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/IrnParams.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.foundation.common.model.Ttl - -data class IrnParams(val tag: Tags, val ttl: Ttl, val prompt: Boolean = false) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/PackageName.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/PackageName.kt deleted file mode 100644 index c2d556464..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/PackageName.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.android.internal.common.model - -@JvmInline -value class PackageName(val value: String) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participants.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participants.kt deleted file mode 100644 index 1a516c618..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Participants.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.foundation.common.model.PublicKey - -data class Participants( - val senderPublicKey: PublicKey, - val receiverPublicKey: PublicKey, -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ProjectId.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ProjectId.kt deleted file mode 100644 index ae5160127..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/ProjectId.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.android.internal.common.model - -data class ProjectId(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SDKError.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SDKError.kt deleted file mode 100644 index 82d9d3085..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SDKError.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -class SDKError(val exception: Throwable) : EngineEvent \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SymmetricKey.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SymmetricKey.kt deleted file mode 100644 index 469363dab..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/SymmetricKey.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.foundation.common.model.Key - -@JvmInline -value class SymmetricKey(override val keyAsHex: String) : Key \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TelemetryEnabled.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TelemetryEnabled.kt deleted file mode 100644 index 344463576..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TelemetryEnabled.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.android.internal.common.model - -@JvmInline -value class TelemetryEnabled(val value: Boolean) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TransportType.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TransportType.kt deleted file mode 100644 index 83247fd8a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/TransportType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.model - -enum class TransportType { - RELAY, - LINK_MODE -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Validation.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Validation.kt deleted file mode 100644 index 25aeefc7b..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/Validation.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.internal.common.model - -enum class Validation { - VALID, - INVALID, - UNKNOWN -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCRequest.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCRequest.kt deleted file mode 100644 index cb3ac8703..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCRequest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty - -data class WCRequest( - val topic: Topic, - val id: Long, - val method: String, - val params: ClientParams, - val message: String = String.Empty, - val publishedAt: Long = 0, - val encryptedMessage: String = String.Empty, - val attestation: String? = null, - val transportType: TransportType -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCResponse.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCResponse.kt deleted file mode 100644 index bbc948193..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/WCResponse.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.internal.common.model - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.foundation.common.model.Topic - -data class WCResponse( - val topic: Topic, - val method: String, - val response: JsonRpcResponse, - val params: ClientParams, -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreAuthParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreAuthParams.kt deleted file mode 100644 index 01c718dad..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreAuthParams.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.android.internal.common.model.params - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.signing.cacao.Cacao - -open class CoreAuthParams : ClientParams { - @JsonClass(generateAdapter = true) - data class ResponseParams( - @Json(name = "h") - val header: Cacao.Header, - @Json(name = "p") - val payload: Cacao.Payload, - @Json(name = "s") - val signature: Cacao.Signature, - ) : CoreAuthParams() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreChatParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreChatParams.kt deleted file mode 100644 index ac91196b3..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreChatParams.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.android.internal.common.model.params - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams - -sealed interface CoreChatParams : ClientParams { - - @JsonClass(generateAdapter = true) - data class ReceiptParams( - @Json(name = "receiptAuth") - val receiptAuth: String, - ) : CoreChatParams -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreSignParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreSignParams.kt deleted file mode 100644 index ca951a95a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/params/CoreSignParams.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.walletconnect.android.internal.common.model.params - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.Participant -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.signing.cacao.Cacao - -open class CoreSignParams : ClientParams { - - @JsonClass(generateAdapter = true) - data class ApprovalParams( - @Json(name = "relay") - val relay: RelayProtocolOptions, - @Json(name = "responderPublicKey") - val responderPublicKey: String, - ) : CoreSignParams() - - @JsonClass(generateAdapter = true) - data class SessionAuthenticateApproveParams( - @Json(name = "responder") - val responder: Participant, - @Json(name = "cacaos") - val cacaos: List, - ) : CoreSignParams() { - val linkMode = responder.metadata.redirect?.linkMode - val appLink = responder.metadata.redirect?.universal - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/sync/ClientJsonRpc.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/sync/ClientJsonRpc.kt deleted file mode 100644 index 950919b02..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/sync/ClientJsonRpc.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.android.internal.common.model.sync - -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -data class ClientJsonRpc( - val id: Long, - val jsonrpc: String, - val method: String -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/ClientParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/ClientParams.kt deleted file mode 100644 index 328e6c97a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/ClientParams.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -interface ClientParams : SerializableJsonRpc \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/EngineEvent.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/EngineEvent.kt deleted file mode 100644 index 5560a815d..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/EngineEvent.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -interface EngineEvent \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Error.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Error.kt deleted file mode 100644 index 73e921926..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Error.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -interface Error { - val message: String - val code: Int -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcClientSync.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcClientSync.kt deleted file mode 100644 index c4e4e80b9..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcClientSync.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -interface JsonRpcClientSync : SerializableJsonRpc { - val id: Long - val method: String - val jsonrpc: String - val params: T -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcInteractorInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcInteractorInterface.kt deleted file mode 100644 index 178e6cbea..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/JsonRpcInteractorInterface.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.WCResponse -import kotlinx.coroutines.flow.SharedFlow - -interface JsonRpcInteractorInterface { - val clientSyncJsonRpc: SharedFlow - val peerResponse: SharedFlow - val internalErrors: SharedFlow -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Sequence.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Sequence.kt deleted file mode 100644 index c675a5c13..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/Sequence.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.foundation.common.model.Topic - -interface Sequence { - val topic: Topic - val expiry: Expiry -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/SerializableJsonRpc.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/SerializableJsonRpc.kt deleted file mode 100644 index b3aa3df0e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/model/type/SerializableJsonRpc.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.android.internal.common.model.type - -interface SerializableJsonRpc \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoType.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoType.kt deleted file mode 100644 index 0e11bf544..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoType.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.android.internal.common.signing.cacao - -enum class CacaoType(val header: String) { - EIP4361("eip4361"), - CAIP222("caip222"); - - fun toHeader(): Cacao.Header = Cacao.Header(this.header) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoVerifier.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoVerifier.kt deleted file mode 100644 index fed197f50..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/CacaoVerifier.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.android.internal.common.signing.cacao - -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.signature.verify -import org.web3j.utils.Numeric - -class CacaoVerifier(private val projectId: ProjectId) { - fun verify(cacao: Cacao): Boolean = when (cacao.signature.t) { - - SignatureType.EIP191.header, SignatureType.EIP1271.header -> { - val plainMessage = cacao.payload.toCAIP222Message() - val hexMessage = Numeric.toHexString(cacao.payload.toCAIP222Message().toByteArray()) - val address = Issuer(cacao.payload.iss).address - - if (cacao.signature.toSignature().verify(plainMessage, address, cacao.signature.t, projectId)) { - true - } else { - cacao.signature.toSignature().verify(hexMessage, address, cacao.signature.t, projectId) - } - } - - else -> throw RuntimeException("Invalid header") - } -} - diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Utils.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Utils.kt deleted file mode 100644 index 7446a822e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/cacao/Utils.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.walletconnect.android.internal.common.signing.cacao - -import android.util.Base64 -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX -import com.walletconnect.utils.HexPrefix -import org.json.JSONArray -import org.json.JSONObject - -@JvmSynthetic -internal fun String.guaranteeNoHexPrefix(): String = removePrefix(String.HexPrefix) - -@JvmSynthetic -fun String?.parseReCaps(): MutableMap>> { - if (this.isNullOrEmpty()) return emptyMap>>().toMutableMap() - val reCapsMap: MutableMap>> = mutableMapOf() - - val jsonObject = JSONObject(this) - val attObject = jsonObject.getJSONObject("att") - - attObject.keys().forEach { key -> - val innerObject = attObject.getJSONObject(key) - val requestsMap = mutableMapOf>() - - innerObject.keys().forEach { requestType -> - val requestArray = innerObject.getJSONArray(requestType) - val dynamicList = mutableListOf() - - for (i in 0 until requestArray.length()) { - val itemObject = requestArray.getJSONObject(i) - // Assuming the structure under each requestType contains arrays of strings - itemObject.keys().forEach { dynamicKey -> - val dynamicArray = itemObject.getJSONArray(dynamicKey) - for (j in 0 until dynamicArray.length()) { - dynamicList.add(dynamicArray.getString(j)) - } - } - } - - requestsMap[requestType] = dynamicList - } - - reCapsMap[key] = requestsMap - } - - return reCapsMap.mapValues { entry -> entry.value.toMutableMap() }.toMutableMap() -} - -@JvmSynthetic -fun List?.decodeReCaps(): String? { - return try { - val last = this?.last() - if (last != null && last.startsWith(RECAPS_PREFIX)) { - Base64.decode(last.removePrefix(RECAPS_PREFIX), Base64.NO_WRAP).toString(Charsets.UTF_8) - } else { - null - } - } catch (e: Exception) { - null - } -} - -@JvmSynthetic -fun List?.getMethods(): List { - return this.decodeReCaps().parseReCaps()["eip155"]?.keys?.sorted()?.map { key -> key.substringAfter('/') } ?: emptyList() -} - -@JvmSynthetic -fun List?.getChains(): List { - return this.decodeReCaps().parseReCaps()["eip155"]?.values?.flatten()?.distinct() ?: emptyList() -} - -@JvmSynthetic -fun mergeReCaps(json1: JSONObject, json2: JSONObject): String { - val result = JSONObject(json1.toString()) // Start with a deep copy of json1 - - json2.keys().forEach { key -> - if (!result.has(key)) { - // If json1 does not have the key, simply put the json2 object/array/primitive - result.put(key, json2.get(key)) - } else { - // If both json1 and json2 have the object, merge them - val value1 = result.get(key) - val value2 = json2.get(key) - - when { - value1 is JSONObject && value2 is JSONObject -> { - result.put(key, mergeReCaps(value1, value2)) - } - - value1 is JSONArray && value2 is JSONArray -> { - // Concatenate arrays, respecting ordering rules if specified - val mergedArray = concatenateJsonArrays(value1, value2) - result.put(key, mergedArray) - } - - else -> { - // For primitive types or if types are different, json2 overrides json1 - result.put(key, value2) - } - } - } - } - return result.toString().replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}") -} - -private fun concatenateJsonArrays(arr1: JSONArray, arr2: JSONArray): JSONArray { - val result = JSONArray() - for (i in 0 until arr1.length()) { - result.put(arr1.get(i)) - } - for (i in 0 until arr2.length()) { - result.put(arr2.get(i)) - } - return result -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/message/MessageSignatureVerifier.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/message/MessageSignatureVerifier.kt deleted file mode 100644 index f46ad42c9..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/message/MessageSignatureVerifier.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.internal.common.signing.message - -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.signature.Signature -import com.walletconnect.android.internal.common.signing.signature.verify - - -class MessageSignatureVerifier(private val projectId: ProjectId) { - fun verify(signature: String, originalMessage: String, address: String, type: SignatureType): Boolean = - Signature.fromString(signature).verify(originalMessage, address, type.header, projectId) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/model/HexString.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/model/HexString.kt deleted file mode 100644 index d26a43a84..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/signing/model/HexString.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.android.internal.common.signing.model - -@JvmInline -value class HexString(val value: String) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyStore.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyStore.kt deleted file mode 100644 index 780393a40..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/key_chain/KeyStore.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.internal.common.storage.key_chain - -import com.walletconnect.foundation.common.model.Key - -interface KeyStore { - fun getKey(tag: String): String? - fun setKey(tag: String, key: Key) - - fun getKeys(tag: String): Pair? - fun setKeys(tag: String, key1: Key, key2: Key) - - fun deleteKeys(tag: String) - fun checkKeys(tag: String): Boolean -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt deleted file mode 100644 index 32a131c8f..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/metadata/MetadataStorageRepositoryInterface.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.walletconnect.android.internal.common.storage.metadata - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.foundation.common.model.Topic - -interface MetadataStorageRepositoryInterface { - - fun insertOrAbortMetadata(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) - - fun updateMetaData(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) - - suspend fun updateOrAbortMetaDataTopic(oldTopic: Topic, newTopic: Topic) - - fun deleteMetaData(topic: Topic) - - fun existsByTopicAndType(topic: Topic, type: AppMetaDataType): Boolean - - fun getByTopicAndType(topic: Topic, type: AppMetaDataType): AppMetaData? - - fun upsertPeerMetadata(topic: Topic, appMetaData: AppMetaData, appMetaDataType: AppMetaDataType) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt deleted file mode 100644 index 62e18fd05..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/common/storage/pairing/PairingStorageRepositoryInterface.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.android.internal.common.storage.pairing - -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.foundation.common.model.Topic - -interface PairingStorageRepositoryInterface { - - fun insertPairing(pairing: Pairing) - - fun deletePairing(topic: Topic) - - fun hasTopic(topic: Topic): Boolean - - suspend fun getListOfPairings(): List - - suspend fun getListOfPairingsWithoutRequestReceived(): List - - fun setRequestReceived(topic: Topic) - - fun updateExpiry(topic: Topic, expiry: Expiry) - - fun getPairingOrNullByTopic(topic: Topic): Pairing? -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/UtilFunctions.kt b/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/UtilFunctions.kt deleted file mode 100644 index 38e2029a2..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/internal/utils/UtilFunctions.kt +++ /dev/null @@ -1,60 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package com.walletconnect.utils - -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.type.SerializableJsonRpc -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import org.koin.core.definition.KoinDefinition -import org.koin.core.module.Module -import org.koin.core.qualifier.named -import org.koin.ext.getFullName -import java.net.URI -import kotlin.reflect.KClass - -@get:JvmSynthetic -val String.Companion.Empty - get() = "" - -@get:JvmSynthetic -val Int.Companion.DefaultId - get() = -1 - -@JvmSynthetic -fun Long.extractTimestamp() = this / 1000 - -@JvmSynthetic -fun Expiry.isSequenceValid(): Boolean = seconds > currentTimeInSeconds - -@get:JvmSynthetic -val String.Companion.HexPrefix - get() = "0x" - -fun Module.addSerializerEntry(value: KClass): KoinDefinition> = - single(qualifier = named("key_${value.getFullName()}")) { value } - -fun Module.addDeserializerEntry(key: String, value: KClass<*>): KoinDefinition>> = - single(qualifier = named("${key}_${value.getFullName()}")) { key to value } - -// Had to add JsonAdapterEntry because Koin would fetch the wrong values when using Pair instead -data class JsonAdapterEntry(val type: Class, val adapter: (Moshi) -> JsonAdapter) { - override fun toString(): String = "JsonAdapterEntry(type=${type.name})" -} - -fun Module.addJsonAdapter(type: Class, adapter: (Moshi) -> JsonAdapter): KoinDefinition> { - val jsonAdapterEntry = JsonAdapterEntry(type, adapter) - return single(qualifier = named("$jsonAdapterEntry")) { jsonAdapterEntry } -} - -@JvmSynthetic -fun compareDomains(metadataUrl: String, originUrl: String): Boolean { - try { - val metadataDomain = URI(metadataUrl).host.removePrefix("www.") - val originDomain = URI(originUrl).host.removePrefix("www.") - return metadataDomain == originDomain - } catch (e: Exception) { - return false - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt deleted file mode 100644 index 7b45fc395..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterIdentityUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerBody - -class RegisterIdentityUseCase( - private val service: KeyServerService, -) { - suspend operator fun invoke(cacao: Cacao): Result = runCatching { - service.registerIdentity(KeyServerBody.RegisterIdentity(cacao)).unwrapUnit() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterInviteUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterInviteUseCase.kt deleted file mode 100644 index 5437f43aa..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/RegisterInviteUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerBody - -class RegisterInviteUseCase( - private val service: KeyServerService, -) { - suspend operator fun invoke(idAuth: String): Result = runCatching { - service.registerInvite(KeyServerBody.RegisterInvite(idAuth)).unwrapUnit() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt deleted file mode 100644 index 9a9f69a0c..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveIdentityUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerResponse - -class ResolveIdentityUseCase( - private val service: KeyServerService, -) { - suspend operator fun invoke(identityKey: String): Result = runCatching { - service.resolveIdentity(identityKey).unwrapValue() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveInviteUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveInviteUseCase.kt deleted file mode 100644 index 3b90665b6..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/ResolveInviteUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerResponse - -class ResolveInviteUseCase( - private val service: KeyServerService -) { - suspend operator fun invoke(accountId: AccountId): Result = runCatching { - service.resolveInvite(accountId.value).unwrapValue() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt deleted file mode 100644 index 4e6b81dda..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterIdentityUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerBody - -class UnregisterIdentityUseCase( - private val service: KeyServerService, -) { - suspend operator fun invoke(idAuth: String): Result = runCatching { - service.unregisterIdentity(KeyServerBody.UnregisterIdentity(idAuth)).unwrapUnit() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt deleted file mode 100644 index d8a50b7bc..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/UnregisterInviteUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.keyserver.data.service.KeyServerService -import com.walletconnect.android.keyserver.model.KeyServerBody - -class UnregisterInviteUseCase( - private val service: KeyServerService, -) { - suspend operator fun invoke(idAuth: String): Result = runCatching { - service.unregisterInvite(KeyServerBody.UnregisterInvite(idAuth)).unwrapUnit() - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/Utils.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/Utils.kt deleted file mode 100644 index cc7f4cc51..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/domain/use_case/Utils.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.walletconnect.android.keyserver.domain.use_case - -import com.walletconnect.android.keyserver.model.KeyServerHttpResponse -import com.walletconnect.android.keyserver.model.KeyServerHttpResponse.Companion.SUCCESS_STATUS -import com.walletconnect.android.keyserver.model.KeyServerResponse -import retrofit2.Response - -@JvmSynthetic -internal fun > Response.unwrapUnit() { - if (isSuccessful && body() != null) { - if (body()!!.status == SUCCESS_STATUS) { - return - } else { - throw Throwable(body()!!.error?.message) - } - } else { - throw Throwable(errorBody()?.string()) - } -} - -@JvmSynthetic -internal fun > Response.unwrapValue(): K { - if (isSuccessful && body() != null) { - if (body()!!.status == SUCCESS_STATUS) { - if (body()!!.value != null) { - return body()!!.value!! - } else { - throw Throwable("Expected value is null") - } - } else { - throw Throwable(body()!!.error?.message) - } - } else { - throw Throwable(errorBody()?.string()) - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerResponse.kt b/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerResponse.kt deleted file mode 100644 index a61825dca..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/keyserver/model/KeyServerResponse.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.android.keyserver.model - -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.signing.cacao.Cacao - -sealed class KeyServerResponse { - - @JsonClass(generateAdapter = true) - data class ResolveInvite(val inviteKey: String) : KeyServerResponse() - - @JsonClass(generateAdapter = true) - data class ResolveIdentity(val cacao: Cacao) : KeyServerResponse() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingProtocol.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingProtocol.kt deleted file mode 100644 index 6888983b2..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/client/PairingProtocol.kt +++ /dev/null @@ -1,183 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.pairing.client - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.Validator -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pairing.engine.domain.PairingEngine -import com.walletconnect.android.pairing.engine.model.EngineDO -import com.walletconnect.android.pairing.model.mapper.toCore -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.relay.RelayConnectionInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout -import org.koin.core.KoinApplication - -internal class PairingProtocol(private val koinApp: KoinApplication = wcKoinApp) : PairingInterface { - private lateinit var pairingEngine: PairingEngine - private val logger: Logger by lazy { koinApp.koin.get() } - private val relayClient: RelayConnectionInterface by lazy { koinApp.koin.get() } - private val insertEventUseCase: InsertTelemetryEventUseCase by lazy { koinApp.koin.get() } - - override fun initialize() { - pairingEngine = koinApp.koin.get() - } - - override fun setDelegate(delegate: PairingInterface.Delegate) { - checkEngineInitialization() - - pairingEngine.engineEvent.onEach { event -> - when (event) { - is EngineDO.PairingDelete -> delegate.onPairingDelete(event.toCore()) - is EngineDO.PairingExpire -> delegate.onPairingExpired(Core.Model.ExpiredPairing(event.pairing.toCore())) - is EngineDO.PairingState -> delegate.onPairingState(Core.Model.PairingState(event.isPairingState)) - } - }.launchIn(scope) - } - - @Throws(IllegalStateException::class) - override fun create(onError: (Core.Model.Error) -> Unit): Core.Model.Pairing? { - checkEngineInitialization() - - return try { - pairingEngine.create({ error -> onError(Core.Model.Error(error)) }) - } catch (e: Exception) { - onError(Core.Model.Error(e)) - null - } - } - - @Throws(IllegalStateException::class) - override fun create(onError: (Core.Model.Error) -> Unit, methods: String): Core.Model.Pairing? { - checkEngineInitialization() - - return try { - pairingEngine.create({ error -> onError(Core.Model.Error(error)) }, methods) - } catch (e: Exception) { - onError(Core.Model.Error(e)) - null - } - } - - @Throws(IllegalStateException::class) - override fun pair( - pair: Core.Params.Pair, - onSuccess: (Core.Params.Pair) -> Unit, - onError: (Core.Model.Error) -> Unit, - ) { - checkEngineInitialization() - - scope.launch(Dispatchers.IO) { - awaitConnection( - { - try { - pairingEngine.pair( - uri = pair.uri, - onSuccess = { onSuccess(pair) }, - onFailure = { error -> onError(Core.Model.Error(error)) } - ) - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - }, - { throwable -> - logger.error(throwable) - onError(Core.Model.Error(Throwable("Pairing error: ${throwable.message}"))) - }) - } - } - - @Deprecated(message = "Disconnect method has been deprecated. It will be removed soon. Pairing will disconnect automatically internally.") - @Throws(IllegalStateException::class) - override fun disconnect(disconnect: Core.Params.Disconnect, onError: (Core.Model.Error) -> Unit) { - checkEngineInitialization() - - try { - pairingEngine.disconnect(disconnect.topic) { error -> onError(Core.Model.Error(error)) } - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - } - - @Deprecated(message = "Disconnect method has been deprecated. It will be removed soon. Pairing will disconnect automatically internally.") - @Throws(IllegalStateException::class) - override fun disconnect(topic: String, onError: (Core.Model.Error) -> Unit) { - checkEngineInitialization() - - try { - pairingEngine.disconnect(topic) { error -> onError(Core.Model.Error(error)) } - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - } - - @Deprecated(message = "Ping method has been deprecated. It will be removed soon. Please use Ping from Web3Wallet or Sign clients.") - @Throws(IllegalStateException::class) - override fun ping(ping: Core.Params.Ping, pairingPing: Core.Listeners.PairingPing?) { - checkEngineInitialization() - - try { - pairingEngine.ping(ping.topic, - onSuccess = { topic -> pairingPing?.onSuccess(Core.Model.Ping.Success(topic)) }, - onFailure = { error -> pairingPing?.onError(Core.Model.Ping.Error(error)) }) - } catch (e: Exception) { - pairingPing?.onError(Core.Model.Ping.Error(e)) - } - } - - @Throws(IllegalStateException::class) - override fun getPairings(): List { - checkEngineInitialization() - - return pairingEngine.getPairings().map { pairing -> pairing.toCore() } - } - - override fun validatePairingUri(uri: String): Boolean { - return try { - Validator.validateWCUri(uri) != null - } catch (e: Exception) { - false - } - } - - private suspend fun awaitConnection(onConnection: () -> Unit, errorLambda: (Throwable) -> Unit = {}) { - try { - withTimeout(60000) { - while (true) { - if (relayClient.isNetworkAvailable.value != null) { - if (relayClient.isNetworkAvailable.value == true) { - if (relayClient.wssConnectionState.value is WSSConnectionState.Connected) { - onConnection() - return@withTimeout - } - } else { - insertEventUseCase(Props(type = EventType.Error.NO_INTERNET_CONNECTION)) - errorLambda(Throwable("No internet connection")) - return@withTimeout - } - } - delay(100) - } - } - } catch (e: Exception) { - insertEventUseCase(Props(type = EventType.Error.NO_WSS_CONNECTION)) - errorLambda(Throwable("Failed to connect: ${e.message}")) - } - } - - @Throws(IllegalStateException::class) - private fun checkEngineInitialization() { - check(::pairingEngine.isInitialized) { - "CoreClient needs to be initialized first using the initialize function" - } - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/model/EngineDO.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/model/EngineDO.kt deleted file mode 100644 index cb0834760..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/engine/model/EngineDO.kt +++ /dev/null @@ -1,23 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.pairing.engine.model - -import com.walletconnect.android.internal.common.model.Pairing - -internal sealed class EngineDO { - - @Deprecated(message = "This data object has been deprecated. It will be removed soon.") - data class PairingDelete( - val topic: String, - val reason: String, - ) : EngineDO() - - @Deprecated(message = "This data object has been deprecated. It will be removed soon.") - data class PairingExpire( - val pairing: Pairing - ) : EngineDO() - - data class PairingState( - val isPairingState: Boolean - ) : EngineDO() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingControllerInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingControllerInterface.kt deleted file mode 100644 index 0b95ea384..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/handler/PairingControllerInterface.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.android.pairing.handler - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.foundation.common.model.Topic -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharedFlow - -interface PairingControllerInterface { - val findWrongMethodsFlow: Flow - val storedPairingFlow: SharedFlow>> - val checkVerifyKeyFlow: SharedFlow - - fun initialize() - - fun setRequestReceived(activate: Core.Params.RequestReceived, onError: (Core.Model.Error) -> Unit = {}) - - fun updateMetadata(updateMetadata: Core.Params.UpdateMetadata, onError: (Core.Model.Error) -> Unit = {}) - - fun deleteAndUnsubscribePairing(deletePairing: Core.Params.Delete, onError: (Core.Model.Error) -> Unit = {}) - - fun register(vararg method: String) - - fun getPairingByTopic(topic: Topic): Pairing? -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingExpiration.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingExpiration.kt deleted file mode 100644 index 542124e64..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingExpiration.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:JvmName("Expiration") - -package com.walletconnect.android.pairing.model - -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds - -val pairingExpiry: Long get() = currentTimeInSeconds + fiveMinutesInSeconds \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingParams.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingParams.kt deleted file mode 100644 index 6f0f4603e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/PairingParams.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.walletconnect.android.pairing.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.utils.DefaultId - -sealed class PairingParams : ClientParams { - - @JsonClass(generateAdapter = true) - class DeleteParams( - @Json(name = "code") - val code: Int = Int.DefaultId, - @Json(name = "message") - val message: String, - ) : PairingParams() - - @Suppress("CanSealedSubClassBeObject") - class PingParams : PairingParams() -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/mapper/PairingMapper.kt b/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/mapper/PairingMapper.kt deleted file mode 100644 index 5eed91204..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pairing/model/mapper/PairingMapper.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.walletconnect.android.pairing.model.mapper - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.android.pairing.engine.model.EngineDO -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty - -@JvmSynthetic -@Deprecated("This mapper has been deprecated. It will be removed soon.") -internal fun EngineDO.PairingDelete.toCore(): Core.Model.DeletedPairing = - Core.Model.DeletedPairing(topic, reason) - -@JvmSynthetic -@Deprecated("This mapper has been deprecated. It will be removed soon.") -internal fun Pairing.toCore(): Core.Model.Pairing = - Core.Model.Pairing( - topic.value, - expiry.seconds, - peerAppMetaData?.toCore(), - relayProtocol, - relayData, - uri, - isActive = true, - methods ?: String.Empty - ) - -@JvmSynthetic -fun Core.Model.Pairing.toPairing(): Pairing = - Pairing( - Topic(topic), - Expiry(expiry), - peerAppMetaData?.toAppMetaData(), - relayProtocol, - relayData, - uri, - methods = registeredMethods - ) - -@JvmSynthetic -internal fun Core.Model.AppMetaData.toAppMetaData() = AppMetaData(name = name, description = description, url = url, icons = icons, redirect = Redirect(redirect)) - -@JvmSynthetic -internal fun AppMetaData?.toCore() = Core.Model.AppMetaData(this?.name ?: String.Empty, this?.description ?: String.Empty, this?.url ?: String.Empty, this?.icons ?: emptyList(), this?.redirect?.native) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendEventUseCase.kt b/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendEventUseCase.kt deleted file mode 100644 index ec7aa81ee..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/domain/SendEventUseCase.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.walletconnect.android.pulse.domain - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.pulse.data.PulseService -import com.walletconnect.android.pulse.model.Event -import com.walletconnect.android.pulse.model.SDKType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.util.generateId -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import org.koin.core.qualifier.named - -class SendEventUseCase( - private val pulseService: PulseService, - private val logger: Logger, - private val bundleId: String, -) : SendEventInterface { - private val enableW3MAnalytics: Boolean by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.ENABLE_WEB_3_MODAL_ANALYTICS)) } - - override fun send(props: Props, sdkType: SDKType, timestamp: Long?, id: Long?) { - if (enableW3MAnalytics) { - scope.launch { - supervisorScope { - try { - val event = Event(props = props, bundleId = bundleId, timestamp = timestamp ?: currentTimeInSeconds, eventId = id ?: generateId()) - val response = pulseService.sendEvent(body = event, sdkType = sdkType.type) - if (!response.isSuccessful) { - logger.error("Failed to send event: ${event.props.type}") - } else { - logger.log("Event sent successfully: ${event.props.type}") - } - } catch (e: Exception) { - logger.error("Failed to send event: ${props.type}, error: $e") - } - } - } - } - } -} - -interface SendEventInterface { - fun send(props: Props, sdkType: SDKType = SDKType.WEB3MODAL, timestamp: Long? = currentTimeInSeconds, id: Long? = generateId()) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Direction.kt b/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Direction.kt deleted file mode 100644 index d48f524dd..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Direction.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.pulse.model - -enum class Direction(val state: String) { - SENT("sent"), - RECEIVED("received") -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Event.kt b/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Event.kt deleted file mode 100644 index 9c1fbeb70..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/Event.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.android.pulse.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.util.generateId - -@JsonClass(generateAdapter = true) -data class Event( - @Json(name = "eventId") - val eventId: Long = generateId(), - @Json(name = "bundleId") - val bundleId: String, - @Json(name = "timestamp") - val timestamp: Long = currentTimeInSeconds, - @Json(name = "props") - val props: Props -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/SDKType.kt b/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/SDKType.kt deleted file mode 100644 index 9539e81e0..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/SDKType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.pulse.model - -enum class SDKType(val type: String) { - WEB3MODAL("w3m"), - EVENTS("events_sdk") -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Props.kt b/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Props.kt deleted file mode 100644 index 0bfb09d8e..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/pulse/model/properties/Props.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.android.pulse.model.properties - -import com.squareup.moshi.Json -import com.walletconnect.android.pulse.model.EventType - -data class Props( - @Json(name = "event") - val event: String = EventType.ERROR, - @Json(name = "type") - val type: String, - @Json(name = "properties") - val properties: Properties? = null -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/network/PushService.kt b/core/android/src/main/kotlin/com/walletconnect/android/push/network/PushService.kt deleted file mode 100644 index f672bab7a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/network/PushService.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.android.push.network - -import com.walletconnect.android.push.network.model.PushBody -import com.walletconnect.android.push.network.model.PushResponse -import retrofit2.Response -import retrofit2.http.* - -interface PushService { - - @POST("{projectId}/clients") - suspend fun register(@Path("projectId") projectId: String, @Query("auth") clientID: String, @Body echoClientsBody: PushBody): Response - - @DELETE("{projectId}/clients/{clientId}") - suspend fun unregister(@Path("projectId") projectId: String, @Path("clientId") clientID: String): Response -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/push/notifications/DecryptMessageUseCaseInterface.kt b/core/android/src/main/kotlin/com/walletconnect/android/push/notifications/DecryptMessageUseCaseInterface.kt deleted file mode 100644 index 26473418c..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/push/notifications/DecryptMessageUseCaseInterface.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.push.notifications - -import com.walletconnect.android.Core - -interface DecryptMessageUseCaseInterface { - suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionState.kt b/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionState.kt deleted file mode 100644 index 6583cd536..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionState.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.relay - -sealed class WSSConnectionState { - object Connected : WSSConnectionState() - - sealed class Disconnected : WSSConnectionState() { - data class ConnectionFailed(val throwable: Throwable) : Disconnected() - data class ConnectionClosed(val message: String? = null) : Disconnected() - } -} diff --git a/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionType.kt b/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionType.kt deleted file mode 100644 index 526004ec9..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/relay/ConnectionType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.android.relay - -enum class ConnectionType { - AUTOMATIC, MANUAL -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/relay/RelayClient.kt b/core/android/src/main/kotlin/com/walletconnect/android/relay/RelayClient.kt deleted file mode 100644 index dd39a8e98..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/relay/RelayClient.kt +++ /dev/null @@ -1,118 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.relay - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.connection.ConnectivityState -import com.walletconnect.android.internal.common.connection.ManualConnectionLifecycle -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.exception.WRONG_CONNECTION_TYPE -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.utils.toWalletConnectException -import com.walletconnect.foundation.network.BaseRelayClient -import com.walletconnect.foundation.network.model.Relay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.takeWhile -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import org.koin.core.KoinApplication -import org.koin.core.qualifier.named - -class RelayClient(private val koinApp: KoinApplication = wcKoinApp) : BaseRelayClient(), RelayConnectionInterface { - private val manualConnection: ManualConnectionLifecycle by lazy { koinApp.koin.get(named(AndroidCommonDITags.MANUAL_CONNECTION_LIFECYCLE)) } - private val networkState: ConnectivityState by lazy { koinApp.koin.get(named(AndroidCommonDITags.CONNECTIVITY_STATE)) } - override val isNetworkAvailable: StateFlow by lazy { networkState.isAvailable } - private val _wssConnectionState: MutableStateFlow = MutableStateFlow(WSSConnectionState.Disconnected.ConnectionClosed()) - override val wssConnectionState: StateFlow = _wssConnectionState - private lateinit var connectionType: ConnectionType - - @JvmSynthetic - fun initialize(connectionType: ConnectionType, onError: (Throwable) -> Unit) { - this.connectionType = connectionType - logger = koinApp.koin.get(named(AndroidCommonDITags.LOGGER)) - relayService = koinApp.koin.get(named(AndroidCommonDITags.RELAY_SERVICE)) - collectConnectionInitializationErrors { error -> onError(error) } - monitorConnectionState() - observeResults() - } - - private fun collectConnectionInitializationErrors(onError: (Throwable) -> Unit) { - scope.launch { - supervisorScope { - eventsFlow - .takeWhile { event -> - if (event is Relay.Model.Event.OnConnectionFailed) { - onError(event.throwable.toWalletConnectException) - } - - event !is Relay.Model.Event.OnConnectionOpened<*> - }.collect() - } - } - } - - private fun monitorConnectionState() { - eventsFlow - .onEach { event: Relay.Model.Event -> setIsWSSConnectionOpened(event) } - .launchIn(scope) - } - - private fun setIsWSSConnectionOpened(event: Relay.Model.Event) { - when { - event is Relay.Model.Event.OnConnectionOpened<*> && _wssConnectionState.value is WSSConnectionState.Disconnected -> - _wssConnectionState.value = WSSConnectionState.Connected - - event is Relay.Model.Event.OnConnectionFailed && _wssConnectionState.value is WSSConnectionState.Connected -> - _wssConnectionState.value = WSSConnectionState.Disconnected.ConnectionFailed(event.throwable) - - event is Relay.Model.Event.OnConnectionClosed && _wssConnectionState.value is WSSConnectionState.Connected -> - _wssConnectionState.value = WSSConnectionState.Disconnected.ConnectionClosed("Connection closed: ${event.shutdownReason.reason} ${event.shutdownReason.code}") - } - } - - override fun connect(onError: (Core.Model.Error) -> Unit) { - when (connectionType) { - ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) - ConnectionType.MANUAL -> manualConnection.connect() - } - } - - override fun disconnect(onError: (Core.Model.Error) -> Unit) { - when (connectionType) { - ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) - ConnectionType.MANUAL -> manualConnection.disconnect() - } - } - - override fun restart(onError: (Core.Model.Error) -> Unit) { - try { - when (connectionType) { - ConnectionType.AUTOMATIC -> onError(Core.Model.Error(IllegalStateException(WRONG_CONNECTION_TYPE))) - ConnectionType.MANUAL -> manualConnection.restart() - } - } catch (e: Exception) { - onError(Core.Model.Error(e)) - } - } - - @Deprecated("This has become deprecate in favor of the onError returning Core.Model.Error", replaceWith = ReplaceWith("this.connect(onErrorModel)")) - override fun connect(onErrorModel: (Core.Model.Error) -> Unit, onError: (String) -> Unit) { - when (connectionType) { - ConnectionType.AUTOMATIC -> onError(WRONG_CONNECTION_TYPE) - ConnectionType.MANUAL -> manualConnection.connect() - } - } - - @Deprecated("This has become deprecate in favor of the onError returning Core.Model.Error", replaceWith = ReplaceWith("this.disconnect(onErrorModel)")) - override fun disconnect(onErrorModel: (Core.Model.Error) -> Unit, onError: (String) -> Unit) { - when (connectionType) { - ConnectionType.AUTOMATIC -> onError(WRONG_CONNECTION_TYPE) - ConnectionType.MANUAL -> manualConnection.disconnect() - } - } -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/utils/Extensions.kt b/core/android/src/main/kotlin/com/walletconnect/android/utils/Extensions.kt deleted file mode 100644 index 2dc2c5f9a..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/utils/Extensions.kt +++ /dev/null @@ -1,64 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.android.utils - -import android.net.Uri -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.exception.GenericException -import com.walletconnect.android.internal.common.exception.InvalidProjectIdException -import com.walletconnect.android.internal.common.exception.ProjectIdDoesNotExistException -import com.walletconnect.android.internal.common.exception.UnableToConnectToWebsocketException -import com.walletconnect.android.internal.common.exception.WalletConnectException -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.utils.Empty -import java.net.HttpURLConnection - -@JvmSynthetic -internal fun String.strippedUrl() = Uri.parse(this).run { - this@run.scheme + "://" + this@run.authority -} - -@JvmSynthetic -internal fun String.isValidRelayServerUrl(): Boolean { - return this.isNotBlank() && Uri.parse(this)?.let { relayUrl -> - arrayOf("wss", "ws").contains(relayUrl.scheme) && !relayUrl.getQueryParameter("projectId").isNullOrBlank() - } ?: false -} - -// Assumes isValidRelayServerUrl returns true. -@JvmSynthetic -internal fun String.projectId(): String { - return Uri.parse(this)!!.let { relayUrl -> - relayUrl.getQueryParameter("projectId")!! - } -} - -@get:JvmSynthetic -internal val Throwable.toWalletConnectException: WalletConnectException - get() = - when { - this.message?.contains(HttpURLConnection.HTTP_UNAUTHORIZED.toString()) == true -> - UnableToConnectToWebsocketException("${this.message}. It's possible that JWT has expired. Try initializing the CoreClient again.") - - this.message?.contains(HttpURLConnection.HTTP_NOT_FOUND.toString()) == true -> - ProjectIdDoesNotExistException(this.message) - - this.message?.contains(HttpURLConnection.HTTP_FORBIDDEN.toString()) == true -> - InvalidProjectIdException(this.message) - - else -> GenericException("Error while connecting, please check your Internet connection or contact support: $this") - } - -@get:JvmSynthetic -val Int.Companion.DefaultId - get() = -1 - -fun AppMetaData?.toClient() = Core.Model.AppMetaData( - name = this?.name ?: String.Empty, - description = this?.description ?: String.Empty, - url = this?.url ?: String.Empty, - icons = this?.icons ?: emptyList(), - redirect = this?.redirect?.native, - appLink = this?.redirect?.universal, - linkMode = this?.redirect?.linkMode ?: false -) diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/data/VerifyService.kt b/core/android/src/main/kotlin/com/walletconnect/android/verify/data/VerifyService.kt deleted file mode 100644 index c1be73770..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/data/VerifyService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.android.verify.data - -import com.walletconnect.android.verify.model.Origin -import com.walletconnect.android.verify.model.RegisterAttestationBody -import com.walletconnect.android.verify.model.VerifyServerPublicKey -import com.walletconnect.android.verify.model.VerifyServerResponse -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.Headers -import retrofit2.http.POST -import retrofit2.http.Path - -interface VerifyService { - @Headers("Content-Type: application/json") - @POST("attestation") - suspend fun registerAttestation(@Body body: RegisterAttestationBody): Response - - @Headers("Content-Type: application/json") - @GET("attestation/{attestationId}?v2Supported=true") - suspend fun resolveAttestation(@Path("attestationId") attestationId: String): Response - - @GET("v2/public-key") - suspend fun getPublicKey(): Response -} \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyResult.kt b/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyResult.kt deleted file mode 100644 index 141e113c7..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyResult.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.android.verify.domain - -import com.walletconnect.android.internal.common.model.Validation - -data class VerifyResult( - val validation: Validation, - val isScam: Boolean?, - val origin: String -) \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyUtils.kt b/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyUtils.kt deleted file mode 100644 index fae14b1e7..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/domain/VerifyUtils.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.android.verify.domain - -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.utils.compareDomains - -fun getValidation(metadataUrl: String, origin: String) = if (compareDomains(metadataUrl, origin)) Validation.VALID else Validation.INVALID \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyContext.kt b/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyContext.kt deleted file mode 100644 index 9d664ea00..000000000 --- a/core/android/src/main/kotlin/com/walletconnect/android/verify/model/VerifyContext.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.android.verify.model - -import com.walletconnect.android.internal.common.model.Validation - -data class VerifyContext( - val id: Long, - val origin: String, - val validation: Validation, - val verifyUrl: String, - val isScam: Boolean? -) \ No newline at end of file diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Event.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Event.sq similarity index 83% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Event.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Event.sq index 9e573c2a4..b94f1a632 100644 --- a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Event.sq +++ b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Event.sq @@ -11,15 +11,16 @@ CREATE TABLE EventDao( trace TEXT AS List, correlation_id INTEGER, client_id TEXT, - direction TEXT + direction TEXT, + user_agent TEXT ); insertOrAbort: -INSERT OR ABORT INTO EventDao(event_id, bundle_id, timestamp, event_name, type, topic, trace, correlation_id, client_id, direction) -VALUES (?,?,?,?,?,?, ?, ?, ?, ?); +INSERT OR ABORT INTO EventDao(event_id, bundle_id, timestamp, event_name, type, topic, trace, correlation_id, client_id, direction, user_agent) +VALUES (?,?,?,?,?,?, ?, ?, ?, ?, ?); getAllEventsWithLimitAndOffset: -SELECT event_id, bundle_id, timestamp, event_name, type, topic, trace, correlation_id, client_id, direction +SELECT event_id, bundle_id, timestamp, event_name, type, topic, trace, correlation_id, client_id, direction, user_agent FROM EventDao ed LIMIT ? OFFSET ?; diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Identities.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Identities.sq similarity index 100% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Identities.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Identities.sq diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/JsonRpcHistory.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/JsonRpcHistory.sq similarity index 94% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/JsonRpcHistory.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/JsonRpcHistory.sq index d200c4374..e9e199f48 100644 --- a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/JsonRpcHistory.sq +++ b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/JsonRpcHistory.sq @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.TransportType; +import com.reown.android.internal.common.model.TransportType; import kotlin.String; CREATE TABLE JsonRpcHistoryDao( diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/MetaData.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/MetaData.sq similarity index 94% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/MetaData.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/MetaData.sq index d36ed4d2b..347f86868 100644 --- a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/MetaData.sq +++ b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/MetaData.sq @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.AppMetaDataType; +import com.reown.android.internal.common.model.AppMetaDataType; import kotlin.Boolean; import kotlin.String; import kotlin.collections.List; diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Pairing.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Pairing.sq similarity index 100% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/Pairing.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/Pairing.sq diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/PushMessage.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/PushMessage.sq similarity index 100% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/PushMessage.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/PushMessage.sq diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/VerifyContext.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/VerifyContext.sq similarity index 90% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/VerifyContext.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/VerifyContext.sq index f48cf8e86..546b4b71d 100644 --- a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/VerifyContext.sq +++ b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/VerifyContext.sq @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.Validation; +import com.reown.android.internal.common.model.Validation; import kotlin.Boolean; CREATE TABLE VerifyContext ( diff --git a/core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/VerifyPublicKey.sq b/core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/VerifyPublicKey.sq similarity index 100% rename from core/android/src/main/sqldelight/com/walletconnect/android/sdk/storage/data/dao/VerifyPublicKey.sq rename to core/android/src/main/sqldelight/com/reown/android/sdk/storage/data/dao/VerifyPublicKey.sq diff --git a/core/android/src/main/sqldelight/databases/12.db b/core/android/src/main/sqldelight/databases/12.db new file mode 100644 index 000000000..21a315fd6 Binary files /dev/null and b/core/android/src/main/sqldelight/databases/12.db differ diff --git a/core/android/src/main/sqldelight/migration/11.sqm b/core/android/src/main/sqldelight/migration/11.sqm new file mode 100644 index 000000000..769f4a610 --- /dev/null +++ b/core/android/src/main/sqldelight/migration/11.sqm @@ -0,0 +1,3 @@ +-- migration from 11.db to 12.db + +ALTER TABLE EventDao ADD COLUMN user_agent TEXT; \ No newline at end of file diff --git a/core/android/src/main/sqldelight/migration/2.sqm b/core/android/src/main/sqldelight/migration/2.sqm index 3532537d9..7a6c7e8dc 100644 --- a/core/android/src/main/sqldelight/migration/2.sqm +++ b/core/android/src/main/sqldelight/migration/2.sqm @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.Validation; +import com.reown.android.internal.common.model.Validation; -- migration from 2.db to 3.db diff --git a/core/android/src/release/kotlin/com.reown.android/di/AndroidBuildVariantDITags.kt b/core/android/src/release/kotlin/com.reown.android/di/AndroidBuildVariantDITags.kt new file mode 100644 index 000000000..bf23225c4 --- /dev/null +++ b/core/android/src/release/kotlin/com.reown.android/di/AndroidBuildVariantDITags.kt @@ -0,0 +1,7 @@ +package com.reown.android.di + +enum class AndroidBuildVariantDITags { + DB_PASSPHRASE, + ANDROID_CORE_DATABASE, + ANDROID_CORE_DATABASE_DRIVER, +} \ No newline at end of file diff --git a/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt b/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt new file mode 100644 index 000000000..1b67f7f08 --- /dev/null +++ b/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt @@ -0,0 +1,206 @@ +package com.reown.android.di + +import android.annotation.SuppressLint +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import android.util.Base64 +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import com.getkeepsafe.relinker.ReLinker +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.di.DatabaseConfig +import com.reown.android.internal.common.di.baseStorageModule +import com.reown.android.internal.common.di.deleteDatabases +import com.reown.android.sdk.core.AndroidCoreDatabase +import com.reown.foundation.util.Logger +import com.reown.util.randomBytes +import com.reown.utils.Empty +import net.sqlcipher.database.SQLiteDatabaseHook +import net.sqlcipher.database.SupportFactory +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.core.scope.Scope +import org.koin.dsl.module +import java.io.File +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec + +private const val ANDROID_KEYSTORE = "AndroidKeyStore" +private const val KEYSTORE_ALIAS = "_wc_db_key_" +private const val SHARED_PREFS_FILENAME = "db_key_store" +private const val KEY_SIZE = 256 +private val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } +private val cipher: Cipher = + "${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_GCM}/${KeyProperties.ENCRYPTION_PADDING_NONE}".let { transformation -> + Cipher.getInstance(transformation) + } +private val keyGenParameterSpec: KeyGenParameterSpec = + KeyGenParameterSpec.Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(KEY_SIZE) + .build() + +@Synchronized +private fun Scope.createSharedPreferences(): SharedPreferences { + val masterKey = MasterKey.Builder(androidContext(), KEYSTORE_ALIAS) + .setKeyGenParameterSpec(keyGenParameterSpec) + .build() + + return EncryptedSharedPreferences.create( + androidContext(), + SHARED_PREFS_FILENAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) +} + +@Synchronized +private fun Scope.deleteSharedPreferences() { + try { + androidContext().run { + if (getSharedPreferences(SHARED_PREFS_FILENAME, Context.MODE_PRIVATE) != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + deleteSharedPreferences(SHARED_PREFS_FILENAME) + } else { + getSharedPreferences(SHARED_PREFS_FILENAME, Context.MODE_PRIVATE).edit().clear().apply() + val dir = File(applicationInfo.dataDir, "shared_prefs") + File(dir, "$SHARED_PREFS_FILENAME.xml").delete() + } + } + } + keyStore.deleteEntry(KEYSTORE_ALIAS) + } catch (e: Exception) { + get(named(AndroidCommonDITags.LOGGER)).error("Occurred when trying to reset encrypted shared prefs: $e") + } +} + +@Synchronized +private fun getSecretKey(): SecretKey { + return (keyStore.getEntry(keyGenParameterSpec.keystoreAlias, null) as? KeyStore.SecretKeyEntry)?.secretKey ?: KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, + ANDROID_KEYSTORE + ).run { + init(keyGenParameterSpec) + generateKey() + } +} + +@Synchronized +private fun signingModule() = module { + single(named(AndroidBuildVariantDITags.DB_PASSPHRASE)) { + val SP_ENCRYPTED_KEY = "encryptedDBKey" + val sharedPreferences: SharedPreferences = try { + createSharedPreferences() + } catch (e: Exception) { + deleteSharedPreferences() + deleteDatabases() + createSharedPreferences() + } + val encryptedDBKeyFromStore: ByteArray? = sharedPreferences.getString(SP_ENCRYPTED_KEY, null)?.let { encryptedDBKey -> + Base64.decode(encryptedDBKey, Base64.DEFAULT) + } + + if (encryptedDBKeyFromStore == null) { + val generatedKeyForDBByteArray = randomBytes(32) + val secretKey: SecretKey = getSecretKey() + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedKey: ByteArray = cipher.doFinal(generatedKeyForDBByteArray) + val iv: ByteArray = cipher.iv + val ivAndEncryptedKey = ByteArray(Integer.BYTES + iv.size + encryptedKey.size) + + ByteBuffer.wrap(ivAndEncryptedKey).run { + order(ByteOrder.BIG_ENDIAN) + putInt(iv.size) + put(iv) + put(encryptedKey) + } + + sharedPreferences.edit().putString(SP_ENCRYPTED_KEY, Base64.encodeToString(ivAndEncryptedKey, Base64.NO_WRAP)).apply() + + generatedKeyForDBByteArray + } else { + val buffer = ByteBuffer.wrap(encryptedDBKeyFromStore).apply { + order(ByteOrder.BIG_ENDIAN) + } + val ivLength = buffer.int + val iv = ByteArray(ivLength).apply { + buffer.get(this) + } + val encryptedKey = ByteArray(encryptedDBKeyFromStore.size - Integer.BYTES - ivLength).apply { + buffer.get(this) + } + + val secretKey: SecretKey = getSecretKey() + val ivSpec = GCMParameterSpec(128, iv) + cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec) + + cipher.doFinal(encryptedKey) + } + } +} + +fun getSupportFactory( + context: Context, + passphrase: ByteArray, + hook: SQLiteDatabaseHook?, + clearPassphrase: Boolean +): SupportFactory { + loadSqlCipherLibrary(context) + return SupportFactory(passphrase, hook, clearPassphrase) +} + +private fun loadSqlCipherLibrary(context: Context) { + val libraryName = "sqlcipher" + try { + System.loadLibrary(libraryName) + } catch (e: UnsatisfiedLinkError) { + ReLinker.loadLibrary(context, libraryName, object : ReLinker.LoadListener { + override fun success() {} + override fun failure(t: Throwable?) { + throw e + } + }) + } +} + +fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { + + includes(baseStorageModule(storagePrefix, bundleId), signingModule()) + + single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { + AndroidSqliteDriver( + schema = AndroidCoreDatabase.Schema, + context = androidContext(), + name = get().ANDROID_CORE_DB_NAME, + factory = getSupportFactory(androidContext(), get(named(AndroidBuildVariantDITags.DB_PASSPHRASE)), null, false) //todo: create a separate DB_PASSHPHRASE + ) + } +} + +@SuppressLint("HardwareIds") +fun sdkBaseStorageModule(databaseSchema: SqlSchema>, databaseName: String) = module { + + single(named(databaseName)) { + AndroidSqliteDriver( + schema = databaseSchema, + context = androidContext(), + name = databaseName, + factory = getSupportFactory(androidContext(), get(named(AndroidBuildVariantDITags.DB_PASSPHRASE)), null, false) + ) + } +} \ No newline at end of file diff --git a/core/android/src/release/kotlin/com.walletconnect.android/di/AndroidBuildVariantDITags.kt b/core/android/src/release/kotlin/com.walletconnect.android/di/AndroidBuildVariantDITags.kt deleted file mode 100644 index 1d05a6a72..000000000 --- a/core/android/src/release/kotlin/com.walletconnect.android/di/AndroidBuildVariantDITags.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.android.di - -enum class AndroidBuildVariantDITags { - DB_PASSPHRASE, - ANDROID_CORE_DATABASE, - ANDROID_CORE_DATABASE_DRIVER, -} \ No newline at end of file diff --git a/core/android/src/release/kotlin/com.walletconnect.android/di/CoreStorageModule.kt b/core/android/src/release/kotlin/com.walletconnect.android/di/CoreStorageModule.kt deleted file mode 100644 index def462799..000000000 --- a/core/android/src/release/kotlin/com.walletconnect.android/di/CoreStorageModule.kt +++ /dev/null @@ -1,206 +0,0 @@ -package com.walletconnect.android.di - -import android.annotation.SuppressLint -import android.content.Context -import android.content.SharedPreferences -import android.os.Build -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties -import android.util.Base64 -import androidx.security.crypto.EncryptedSharedPreferences -import androidx.security.crypto.MasterKey -import app.cash.sqldelight.db.QueryResult -import app.cash.sqldelight.db.SqlDriver -import app.cash.sqldelight.db.SqlSchema -import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import com.getkeepsafe.relinker.ReLinker -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.di.DatabaseConfig -import com.walletconnect.android.internal.common.di.baseStorageModule -import com.walletconnect.android.internal.common.di.deleteDatabases -import com.walletconnect.android.sdk.core.AndroidCoreDatabase -import com.walletconnect.foundation.util.Logger -import com.walletconnect.util.randomBytes -import com.walletconnect.utils.Empty -import net.sqlcipher.database.SQLiteDatabaseHook -import net.sqlcipher.database.SupportFactory -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.core.scope.Scope -import org.koin.dsl.module -import java.io.File -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.security.KeyStore -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey -import javax.crypto.spec.GCMParameterSpec - -private const val ANDROID_KEYSTORE = "AndroidKeyStore" -private const val KEYSTORE_ALIAS = "_wc_db_key_" -private const val SHARED_PREFS_FILENAME = "db_key_store" -private const val KEY_SIZE = 256 -private val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) } -private val cipher: Cipher = - "${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_GCM}/${KeyProperties.ENCRYPTION_PADDING_NONE}".let { transformation -> - Cipher.getInstance(transformation) - } -private val keyGenParameterSpec: KeyGenParameterSpec = - KeyGenParameterSpec.Builder(KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(KEY_SIZE) - .build() - -@Synchronized -private fun Scope.createSharedPreferences(): SharedPreferences { - val masterKey = MasterKey.Builder(androidContext(), KEYSTORE_ALIAS) - .setKeyGenParameterSpec(keyGenParameterSpec) - .build() - - return EncryptedSharedPreferences.create( - androidContext(), - SHARED_PREFS_FILENAME, - masterKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) -} - -@Synchronized -private fun Scope.deleteSharedPreferences() { - try { - androidContext().run { - if (getSharedPreferences(SHARED_PREFS_FILENAME, Context.MODE_PRIVATE) != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - deleteSharedPreferences(SHARED_PREFS_FILENAME) - } else { - getSharedPreferences(SHARED_PREFS_FILENAME, Context.MODE_PRIVATE).edit().clear().apply() - val dir = File(applicationInfo.dataDir, "shared_prefs") - File(dir, "$SHARED_PREFS_FILENAME.xml").delete() - } - } - } - keyStore.deleteEntry(KEYSTORE_ALIAS) - } catch (e: Exception) { - get(named(AndroidCommonDITags.LOGGER)).error("Occurred when trying to reset encrypted shared prefs: $e") - } -} - -@Synchronized -private fun getSecretKey(): SecretKey { - return (keyStore.getEntry(keyGenParameterSpec.keystoreAlias, null) as? KeyStore.SecretKeyEntry)?.secretKey ?: KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, - ANDROID_KEYSTORE - ).run { - init(keyGenParameterSpec) - generateKey() - } -} - -@Synchronized -private fun signingModule() = module { - single(named(AndroidBuildVariantDITags.DB_PASSPHRASE)) { - val SP_ENCRYPTED_KEY = "encryptedDBKey" - val sharedPreferences: SharedPreferences = try { - createSharedPreferences() - } catch (e: Exception) { - deleteSharedPreferences() - deleteDatabases() - createSharedPreferences() - } - val encryptedDBKeyFromStore: ByteArray? = sharedPreferences.getString(SP_ENCRYPTED_KEY, null)?.let { encryptedDBKey -> - Base64.decode(encryptedDBKey, Base64.DEFAULT) - } - - if (encryptedDBKeyFromStore == null) { - val generatedKeyForDBByteArray = randomBytes(32) - val secretKey: SecretKey = getSecretKey() - cipher.init(Cipher.ENCRYPT_MODE, secretKey) - - val encryptedKey: ByteArray = cipher.doFinal(generatedKeyForDBByteArray) - val iv: ByteArray = cipher.iv - val ivAndEncryptedKey = ByteArray(Integer.BYTES + iv.size + encryptedKey.size) - - ByteBuffer.wrap(ivAndEncryptedKey).run { - order(ByteOrder.BIG_ENDIAN) - putInt(iv.size) - put(iv) - put(encryptedKey) - } - - sharedPreferences.edit().putString(SP_ENCRYPTED_KEY, Base64.encodeToString(ivAndEncryptedKey, Base64.NO_WRAP)).apply() - - generatedKeyForDBByteArray - } else { - val buffer = ByteBuffer.wrap(encryptedDBKeyFromStore).apply { - order(ByteOrder.BIG_ENDIAN) - } - val ivLength = buffer.int - val iv = ByteArray(ivLength).apply { - buffer.get(this) - } - val encryptedKey = ByteArray(encryptedDBKeyFromStore.size - Integer.BYTES - ivLength).apply { - buffer.get(this) - } - - val secretKey: SecretKey = getSecretKey() - val ivSpec = GCMParameterSpec(128, iv) - cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec) - - cipher.doFinal(encryptedKey) - } - } -} - -fun getSupportFactory( - context: Context, - passphrase: ByteArray, - hook: SQLiteDatabaseHook?, - clearPassphrase: Boolean -): SupportFactory { - loadSqlCipherLibrary(context) - return SupportFactory(passphrase, hook, clearPassphrase) -} - -private fun loadSqlCipherLibrary(context: Context) { - val libraryName = "sqlcipher" - try { - System.loadLibrary(libraryName) - } catch (e: UnsatisfiedLinkError) { - ReLinker.loadLibrary(context, libraryName, object : ReLinker.LoadListener { - override fun success() {} - override fun failure(t: Throwable?) { - throw e - } - }) - } -} - -fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { - - includes(baseStorageModule(storagePrefix, bundleId), signingModule()) - - single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { - AndroidSqliteDriver( - schema = AndroidCoreDatabase.Schema, - context = androidContext(), - name = get().ANDROID_CORE_DB_NAME, - factory = getSupportFactory(androidContext(), get(named(AndroidBuildVariantDITags.DB_PASSPHRASE)), null, false) //todo: create a separate DB_PASSHPHRASE - ) - } -} - -@SuppressLint("HardwareIds") -fun sdkBaseStorageModule(databaseSchema: SqlSchema>, databaseName: String) = module { - - single(named(databaseName)) { - AndroidSqliteDriver( - schema = databaseSchema, - context = androidContext(), - name = databaseName, - factory = getSupportFactory(androidContext(), get(named(AndroidBuildVariantDITags.DB_PASSPHRASE)), null, false) - ) - } -} \ No newline at end of file diff --git a/core/android/src/test/java/CacaoTestJvmTest.java b/core/android/src/test/java/CacaoTestJvmTest.java index fb5713b70..9011afd40 100644 --- a/core/android/src/test/java/CacaoTestJvmTest.java +++ b/core/android/src/test/java/CacaoTestJvmTest.java @@ -1,11 +1,11 @@ -import com.walletconnect.android.cacao.signature.SignatureType; -import com.walletconnect.android.internal.common.model.ProjectId; -import com.walletconnect.android.internal.common.signing.cacao.Cacao; -import com.walletconnect.android.internal.common.signing.cacao.CacaoKt; -import com.walletconnect.android.internal.common.signing.cacao.CacaoType; -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier; -import com.walletconnect.android.utils.cacao.CacaoSignerUtil; -import com.walletconnect.util.UtilFunctionsKt; +import com.reown.android.cacao.signature.SignatureType; +import com.reown.android.internal.common.model.ProjectId; +import com.reown.android.internal.common.signing.cacao.Cacao; +import com.reown.android.internal.common.signing.cacao.CacaoKt; +import com.reown.android.internal.common.signing.cacao.CacaoType; +import com.reown.android.internal.common.signing.cacao.CacaoVerifier; +import com.reown.android.utils.cacao.CacaoSignerUtil; +import com.reown.util.UtilFunctionsKt; import org.junit.Assert; import org.junit.Test; diff --git a/core/android/src/test/java/SignatureTest.java b/core/android/src/test/java/SignatureTest.java index 8d7263bf1..ebcdb0109 100644 --- a/core/android/src/test/java/SignatureTest.java +++ b/core/android/src/test/java/SignatureTest.java @@ -1,4 +1,4 @@ -import com.walletconnect.android.cacao.SignatureInterface; +import com.reown.android.cacao.SignatureInterface; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/BouncyCastleCryptoRepositoryTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/BouncyCastleCryptoRepositoryTest.kt similarity index 91% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/BouncyCastleCryptoRepositoryTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/BouncyCastleCryptoRepositoryTest.kt index 078c6d22e..1fe6fff95 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/BouncyCastleCryptoRepositoryTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/BouncyCastleCryptoRepositoryTest.kt @@ -1,11 +1,11 @@ -package com.walletconnect.android.internal - -import com.walletconnect.android.internal.common.crypto.kmr.BouncyCastleKeyManagementRepository -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty +package com.reown.android.internal + +import com.reown.android.internal.common.crypto.kmr.BouncyCastleKeyManagementRepository +import com.reown.android.internal.common.storage.key_chain.KeyStore +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty import io.mockk.spyk import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/ChaChaPolyCodecTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/ChaChaPolyCodecTest.kt similarity index 85% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/ChaChaPolyCodecTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/ChaChaPolyCodecTest.kt index 178cc1914..c1c271bd6 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/ChaChaPolyCodecTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/ChaChaPolyCodecTest.kt @@ -1,16 +1,16 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.internal.common.crypto.codec.ChaChaPolyCodec -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.MissingParticipantsException -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty +import com.reown.android.internal.common.crypto.codec.ChaChaPolyCodec +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.MissingKeyException +import com.reown.android.internal.common.model.MissingParticipantsException +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.utils.getParticipantTag +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty import io.mockk.every import io.mockk.mockk import junit.framework.TestCase.assertEquals diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/CoreValidatorTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/CoreValidatorTest.kt similarity index 93% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/CoreValidatorTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/CoreValidatorTest.kt index abf042a36..699ea1aab 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/CoreValidatorTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/CoreValidatorTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.CoreValidator +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.CoreValidator import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/EventsRepositoryTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/EventsRepositoryTest.kt similarity index 85% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/EventsRepositoryTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/EventsRepositoryTest.kt index e84b3f985..dc60c212f 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/EventsRepositoryTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/EventsRepositoryTest.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.internal +package com.reown.android.internal import android.database.sqlite.SQLiteException -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.sdk.storage.data.dao.EventQueries +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.model.properties.Props +import com.reown.android.sdk.storage.data.dao.EventQueries import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify @@ -46,7 +46,7 @@ class EventsRepositoryTest { @Test fun `insertOrAbort should insert event when telemetry is enabled`() = runTest(testDispatcher) { val props = Props(event = "testEvent", type = "testType") - every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } just Runs + every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } just Runs repository.insertOrAbortTelemetry(props) @@ -61,7 +61,8 @@ class EventsRepositoryTest { trace = null, correlation_id = any(), client_id = any(), - direction = any() + direction = any(), + user_agent = any() ) } } @@ -69,7 +70,7 @@ class EventsRepositoryTest { @Test fun `insertOrAbort should insert event `() = runTest(testDispatcher) { val props = Props(event = "testEvent", type = "testType") - every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } just Runs + every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } just Runs repository.insertOrAbort(props) @@ -84,7 +85,8 @@ class EventsRepositoryTest { trace = null, correlation_id = any(), client_id = any(), - direction = any() + direction = any(), + user_agent = any() ) } } @@ -97,14 +99,14 @@ class EventsRepositoryTest { repository.insertOrAbortTelemetry(props) verify(exactly = 0) { - eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) + eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } } @Test fun `insertOrAbort should throw SQLiteException when insertion fails`() = runTest(testDispatcher) { val props = Props(event = "testEvent", type = "testType") - every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } throws SQLiteException() + every { eventQueries.insertOrAbort(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()) } throws SQLiteException() assertFailsWith { repository.insertOrAbortTelemetry(props) diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/InsertEventUseCaseTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/InsertEventUseCaseTest.kt similarity index 83% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/InsertEventUseCaseTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/InsertEventUseCaseTest.kt index a63787c4a..fe2e71ffd 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/InsertEventUseCaseTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/InsertEventUseCaseTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger +package com.reown.android.internal +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/InsertTelemetryEventUseCaseTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/InsertTelemetryEventUseCaseTest.kt similarity index 86% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/InsertTelemetryEventUseCaseTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/InsertTelemetryEventUseCaseTest.kt index ef53b7d2e..465914915 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/InsertTelemetryEventUseCaseTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/InsertTelemetryEventUseCaseTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/JWTRepositoryTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/JWTRepositoryTest.kt similarity index 94% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/JWTRepositoryTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/JWTRepositoryTest.kt index 7cff344af..7748f6bad 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/JWTRepositoryTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/JWTRepositoryTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.verify.domain.JWTRepository -import com.walletconnect.android.verify.model.JWK -import com.walletconnect.util.hexToBytes +import com.reown.android.verify.domain.JWTRepository +import com.reown.android.verify.model.JWK +import com.reown.util.hexToBytes import io.mockk.spyk import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull diff --git a/core/android/src/test/kotlin/com/reown/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt new file mode 100644 index 000000000..d547333df --- /dev/null +++ b/core/android/src/test/kotlin/com/reown/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt @@ -0,0 +1,123 @@ +package com.reown.android.internal + +import com.squareup.moshi.Moshi +import com.tinder.scarlet.utils.getRawType +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.adapter.JsonRpcResultAdapter +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.signing.cacao.Cacao +import org.junit.Test +import kotlin.reflect.jvm.jvmName + +internal class JsonRpcResponseJsonRpcResultJsonAdapterTest { + + @Test + fun `test sign params to json`() { + val moshi = Moshi.Builder().add { type, _, moshi -> + return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { + JsonRpcResultAdapter(moshi = moshi) + } else { + null + } + }.build() + val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) + val approvalParams = + CoreSignParams.ApprovalParams(relay = RelayProtocolOptions("irn"), responderPublicKey = "124") + val jsonResult = JsonRpcResponse.JsonRpcResult( + id = 1L, + jsonrpc = "2.0", + result = approvalParams + ) + + val result = adapter.toJson(jsonResult) + println() + println(result) + println() + } + + @Test + fun `test sign params from json`() { + val moshi = Moshi.Builder().add { type, _, moshi -> + return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { + JsonRpcResultAdapter(moshi = moshi) + } else { + null + } + }.build() + val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) + val approvalParamsJsonResult = JsonRpcResponse.JsonRpcResult( + id = 11L, + result = CoreSignParams.ApprovalParams(relay = RelayProtocolOptions("irn"), responderPublicKey = "124") + ) + val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(approvalParamsJsonResult) + val result = adapter.fromJson(resultString) + result is JsonRpcResponse.JsonRpcResult + println() + println(result) + println() + } + + @Test + fun `test chat params to json`() { + val moshi = Moshi.Builder().add { type, _, moshi -> + return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { + JsonRpcResultAdapter(moshi = moshi) + } else { + null + } + }.build() + val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) + val chatParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = "did.jwt.auth") + val jsonResult = JsonRpcResponse.JsonRpcResult( + id = 1L, + jsonrpc = "2.0", + result = chatParams + ) + val result = adapter.toJson(jsonResult) + println() + println(result) + println() + } + + @Test + fun `test chat params from json`() { + val moshi = Moshi.Builder().add { type, _, moshi -> + return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { + JsonRpcResultAdapter(moshi = moshi) + } else { + null + } + }.build() + val chatParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = "did.jwt.auth") + val authParamsJsonResult = JsonRpcResponse.JsonRpcResult(id = 11L, result = chatParams) + val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(authParamsJsonResult) + val result = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).fromJson(resultString) + result is JsonRpcResponse.JsonRpcResult + println() + println(result) + println() + } + + @Test + fun `test from json with boolean`() { + val moshi = Moshi.Builder().add { type, _, moshi -> + return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { + JsonRpcResultAdapter(moshi = moshi) + } else { + null + } + }.build() + val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) + + val approvalParamsJsonResult = JsonRpcResponse.JsonRpcResult(id = 11L, result = true) + val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(approvalParamsJsonResult) + + val result = adapter.fromJson(resultString) + result is JsonRpcResponse.JsonRpcResult + println() + println(result) + println() + } +} \ No newline at end of file diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/KeyChainMock.kt b/core/android/src/test/kotlin/com/reown/android/internal/KeyChainMock.kt similarity index 83% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/KeyChainMock.kt rename to core/android/src/test/kotlin/com/reown/android/internal/KeyChainMock.kt index 8beb25bf9..3666f8865 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/KeyChainMock.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/KeyChainMock.kt @@ -1,9 +1,9 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore -import com.walletconnect.foundation.common.model.Key -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes +import com.reown.android.internal.common.storage.key_chain.KeyStore +import com.reown.foundation.common.model.Key +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes internal class KeyChainMock : KeyStore { private val mapOfKeys = mutableMapOf() diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/RelayClientTests.kt b/core/android/src/test/kotlin/com/reown/android/internal/RelayClientTests.kt similarity index 76% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/RelayClientTests.kt rename to core/android/src/test/kotlin/com/reown/android/internal/RelayClientTests.kt index c978a77fc..c2b151773 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/RelayClientTests.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/RelayClientTests.kt @@ -1,13 +1,15 @@ -package com.walletconnect.android.internal +package com.reown.android.internal import com.tinder.scarlet.WebSocket -import com.walletconnect.android.internal.common.connection.ConnectivityState -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayClient -import com.walletconnect.foundation.network.data.service.RelayService -import com.walletconnect.foundation.util.Logger +import com.reown.android.internal.common.connection.ConnectivityState +import com.reown.android.internal.common.connection.DefaultConnectionLifecycle +import com.reown.android.internal.common.connection.ManualConnectionLifecycle +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.scope +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayClient +import com.reown.foundation.network.data.service.RelayService +import com.reown.foundation.util.Logger import io.mockk.clearAllMocks import io.mockk.coVerify import io.mockk.every @@ -34,6 +36,8 @@ import org.koin.dsl.module class RelayClientTests { private lateinit var relayClient: RelayClient private val mockRelayService = mockk(relaxed = true) + private val defaultConnectionLifecycleMock = mockk(relaxed = true) + private val manualConnectionLifecycleMock = mockk(relaxed = true) private val mockLogger = mockk(relaxed = true) private val mockNetworkState = mockk(relaxed = true) private val testDispatcher = StandardTestDispatcher() @@ -47,6 +51,8 @@ class RelayClientTests { single(named(AndroidCommonDITags.RELAY_SERVICE)) { mockRelayService } single(named(AndroidCommonDITags.LOGGER)) { mockLogger } single(named(AndroidCommonDITags.CONNECTIVITY_STATE)) { mockNetworkState } + single(named(AndroidCommonDITags.MANUAL_CONNECTION_LIFECYCLE)) { manualConnectionLifecycleMock } + single(named(AndroidCommonDITags.DEFAULT_CONNECTION_LIFECYCLE)) { defaultConnectionLifecycleMock } }) } @@ -73,7 +79,7 @@ class RelayClientTests { relayClient.initialize(ConnectionType.MANUAL) { error -> assertEquals( - "Error while connecting, please check your Internet connection or contact support: java.lang.Throwable: Network failure", + "Error while connecting, please check your Internet connection or contact support: Network failure", error.message ) scope.coroutineContext.cancelChildren() diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/ResolveAttestationUseCaseTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/ResolveAttestationUseCaseTest.kt similarity index 90% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/ResolveAttestationUseCaseTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/ResolveAttestationUseCaseTest.kt index 5320f20ea..ca8108298 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/ResolveAttestationUseCaseTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/ResolveAttestationUseCaseTest.kt @@ -1,17 +1,17 @@ -package com.walletconnect.android.internal - -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.verify.client.VerifyInterface -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.android.verify.domain.VerifyResult -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty +package com.reown.android.internal + +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.verify.client.VerifyInterface +import com.reown.android.verify.domain.ResolveAttestationIdUseCase +import com.reown.android.verify.domain.VerifyResult +import com.reown.android.verify.model.VerifyContext +import com.reown.foundation.common.model.Topic +import com.reown.utils.Empty import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/SendBatchEventUseCaseTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/SendBatchEventUseCaseTest.kt similarity index 91% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/SendBatchEventUseCaseTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/SendBatchEventUseCaseTest.kt index 04a8e17b2..e91d15c42 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/SendBatchEventUseCaseTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/SendBatchEventUseCaseTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.android.internal - -import com.walletconnect.android.internal.common.model.TelemetryEnabled -import com.walletconnect.android.internal.common.storage.events.EventsRepository -import com.walletconnect.android.pulse.data.PulseService -import com.walletconnect.android.pulse.domain.SendBatchEventUseCase -import com.walletconnect.android.pulse.model.Event -import com.walletconnect.android.pulse.model.SDKType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger +package com.reown.android.internal + +import com.reown.android.internal.common.model.TelemetryEnabled +import com.reown.android.internal.common.storage.events.EventsRepository +import com.reown.android.pulse.data.PulseService +import com.reown.android.pulse.domain.SendBatchEventUseCase +import com.reown.android.pulse.model.Event +import com.reown.android.pulse.model.SDKType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/SendEventUseCaseTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/SendEventUseCaseTest.kt similarity index 88% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/SendEventUseCaseTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/SendEventUseCaseTest.kt index c664bd174..def4cf8dc 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/SendEventUseCaseTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/SendEventUseCaseTest.kt @@ -1,13 +1,13 @@ -package com.walletconnect.android.internal - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.data.PulseService -import com.walletconnect.android.pulse.domain.SendEventUseCase -import com.walletconnect.android.pulse.model.Event -import com.walletconnect.android.pulse.model.SDKType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger +package com.reown.android.internal + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.data.PulseService +import com.reown.android.pulse.domain.SendEventUseCase +import com.reown.android.pulse.model.Event +import com.reown.android.pulse.model.SDKType +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify @@ -66,7 +66,7 @@ class SendEventUseCaseTest : KoinTest { wcKoinApp = app val props = Props(type = "testEvent") - val sdkType = SDKType.WEB3MODAL + val sdkType = SDKType.APPKIT val event = Event(props = props, bundleId = bundleId) coEvery { pulseService.sendEvent(body = any(), sdkType = any()) } returns Response.success(Unit) @@ -92,7 +92,7 @@ class SendEventUseCaseTest : KoinTest { wcKoinApp = app val props = Props(type = "testEvent") - val sdkType = SDKType.WEB3MODAL + val sdkType = SDKType.APPKIT val event = Event(props = props, bundleId = bundleId) coEvery { pulseService.sendEvent(any(), any()) } returns Response.error(400, "ResponseBody".toResponseBody()) @@ -118,7 +118,7 @@ class SendEventUseCaseTest : KoinTest { wcKoinApp = app val props = Props(type = "testEvent") - val sdkType = SDKType.WEB3MODAL + val sdkType = SDKType.APPKIT val event = Event(props = props, bundleId = bundleId) val exception = Exception("Test exception") @@ -145,7 +145,7 @@ class SendEventUseCaseTest : KoinTest { wcKoinApp = app2 val props = Props(type = "testEvent") - val sdkType = SDKType.WEB3MODAL + val sdkType = SDKType.APPKIT val event = Event(props = props, bundleId = bundleId) useCase.send(props, sdkType, event.timestamp, event.eventId) diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/URLComparisonTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/URLComparisonTest.kt similarity index 93% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/URLComparisonTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/URLComparisonTest.kt index 805d039f5..768418518 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/URLComparisonTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/URLComparisonTest.kt @@ -1,7 +1,7 @@ -package com.walletconnect.android.internal +package com.reown.android.internal import com.google.common.net.InternetDomainName -import com.walletconnect.utils.compareDomains +import com.reown.utils.compareDomains import org.junit.Test import java.net.URI diff --git a/core/android/src/test/kotlin/com/reown/android/internal/ValidatorTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/ValidatorTest.kt new file mode 100644 index 000000000..d0defa648 --- /dev/null +++ b/core/android/src/test/kotlin/com/reown/android/internal/ValidatorTest.kt @@ -0,0 +1,115 @@ +package com.reown.android.internal + +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.WalletConnectUri +import com.reown.foundation.common.model.Topic +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertNull +import org.junit.Test + +internal class ValidatorTest { + + @Test + fun `validate with encoded uri query parameter`() { + val validUri = + "deeplink://wc?uri=wc%3A7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9%402%3Frelay-protocol%3Dirn%26symKey%3D587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + + Validator.validateWCUri("").apply { assertEquals(null, this) } + Validator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + } + } + + @Test + fun `validate with decoded uri query parameter`() { + val validUri = + "deeplink://wc?uri=wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + + Validator.validateWCUri("").apply { assertEquals(null, this) } + Validator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + } + } + + @Test + fun `validate WC uri test`() { + val validUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303&expiryTimestamp=1705667684" + + Validator.validateWCUri("").apply { assertEquals(null, this) } + Validator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + assertEquals(Expiry(1705667684L), expiry) + } + + val noTopicInvalidUri = + "wc:@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + Validator.validateWCUri(noTopicInvalidUri).apply { assertNull(this) } + + val noPrefixInvalidUri = + "7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + Validator.validateWCUri(noPrefixInvalidUri).apply { assertNull(this) } + + val noSymKeyInvalidUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=" + Validator.validateWCUri(noSymKeyInvalidUri).apply { assertNull(this) } + + val noProtocolTypeInvalidUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + Validator.validateWCUri(noProtocolTypeInvalidUri).apply { assertNull(this) } + } + + @Test + fun `validate WC uri test optional data field`() { + val validUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&relay-data=testData&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + + Validator.validateWCUri("").apply { assertEquals(null, this) } + Validator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("testData", this.relay.data) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + } + } + + @Test + fun `parse walletconnect uri to absolute string`() { + val uri = WalletConnectUri( + Topic("11112222244444"), + SymmetricKey("0x12321321312312312321"), + RelayProtocolOptions("irn", "teeestData"), + expiry = null, + methods = "wc_method" + ) + + assertEquals(uri.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&relay-data=teeestData&methods=wc_method&symKey=0x12321321312312312321") + + val uri2 = WalletConnectUri( + Topic("11112222244444"), + SymmetricKey("0x12321321312312312321"), + RelayProtocolOptions("irn"), + expiry = Expiry(1705667684), + methods = "" + ) + + assertEquals(uri2.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&expiryTimestamp=1705667684&symKey=0x12321321312312312321") + } +} \ No newline at end of file diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyClientTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/VerifyClientTest.kt similarity index 89% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyClientTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/VerifyClientTest.kt index f08129915..78401cc26 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyClientTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/VerifyClientTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.android.internal +package com.reown.android.internal -import com.walletconnect.android.verify.client.VerifyClient -import com.walletconnect.android.verify.domain.VerifyRepository -import com.walletconnect.android.verify.domain.VerifyResult +import com.reown.android.verify.client.VerifyClient +import com.reown.android.verify.domain.VerifyRepository +import com.reown.android.verify.domain.VerifyResult import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.test.StandardTestDispatcher diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyRepositoryTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/VerifyRepositoryTest.kt similarity index 91% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyRepositoryTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/VerifyRepositoryTest.kt index 693cfc692..8dc5b75c2 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/VerifyRepositoryTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/VerifyRepositoryTest.kt @@ -1,16 +1,16 @@ -package com.walletconnect.android.internal +package com.reown.android.internal import com.squareup.moshi.Moshi -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.verify.data.VerifyService -import com.walletconnect.android.verify.domain.JWTRepository -import com.walletconnect.android.verify.domain.VerifyPublicKeyStorageRepository -import com.walletconnect.android.verify.domain.VerifyRepository -import com.walletconnect.android.verify.domain.VerifyResult -import com.walletconnect.android.verify.model.JWK -import com.walletconnect.android.verify.model.Origin -import com.walletconnect.android.verify.model.VerifyServerPublicKey +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.verify.data.VerifyService +import com.reown.android.verify.domain.JWTRepository +import com.reown.android.verify.domain.VerifyPublicKeyStorageRepository +import com.reown.android.verify.domain.VerifyRepository +import com.reown.android.verify.domain.VerifyResult +import com.reown.android.verify.model.JWK +import com.reown.android.verify.model.Origin +import com.reown.android.verify.model.VerifyServerPublicKey import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt similarity index 82% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt index 892772dfa..3bad3b9f3 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/common/ClientIdJwtRepositoryAndroidTest.kt @@ -1,10 +1,10 @@ -package com.walletconnect.android.internal.common +package com.reown.android.internal.common -import com.walletconnect.android.internal.KeyChainMock -import com.walletconnect.android.internal.common.jwt.clientid.ClientIdJwtRepositoryAndroid -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.jwtIatAndExp +import com.reown.android.internal.KeyChainMock +import com.reown.android.internal.common.jwt.clientid.ClientIdJwtRepositoryAndroid +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.jwtIatAndExp import io.mockk.every import io.mockk.mockkStatic import io.mockk.spyk diff --git a/core/android/src/test/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapterTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapterTest.kt new file mode 100644 index 000000000..dff7891ef --- /dev/null +++ b/core/android/src/test/kotlin/com/reown/android/internal/common/adapter/ExpiryAdapterTest.kt @@ -0,0 +1,25 @@ +package com.reown.android.internal.common.adapter + +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import junit.framework.TestCase.assertEquals +import org.junit.Test + +internal class ExpiryAdapterTest { + private val moshi = Moshi.Builder() + .add { _, _, _ -> + ExpiryAdapter + } + .add(KotlinJsonAdapterFactory()) + .build() + + @Test + fun toJson() { + val expiry = com.reown.android.internal.common.model.Expiry(100L) + val expected = """"${expiry.seconds}"""" + + val expiryJson = moshi.adapter(com.reown.android.internal.common.model.Expiry::class.java).toJson(expiry) + + assertEquals(expected, """"$expiryJson"""") + } +} \ No newline at end of file diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/IssuerTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/common/cacao/IssuerTest.kt similarity index 87% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/IssuerTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/common/cacao/IssuerTest.kt index edf4aea91..809353333 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/IssuerTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/common/cacao/IssuerTest.kt @@ -1,6 +1,6 @@ -package com.walletconnect.android.internal.common.cacao +package com.reown.android.internal.common.cacao -import com.walletconnect.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.common.signing.cacao.Issuer import junit.framework.TestCase.assertEquals import org.junit.Assert.assertThrows import org.junit.Test diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/eip191/EIP191SignerTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/common/cacao/eip191/EIP191SignerTest.kt similarity index 84% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/eip191/EIP191SignerTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/common/cacao/eip191/EIP191SignerTest.kt index fd1b502b9..264ae2324 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/cacao/eip191/EIP191SignerTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/common/cacao/eip191/EIP191SignerTest.kt @@ -1,19 +1,19 @@ -package com.walletconnect.android.internal.common.cacao.eip191 +package com.reown.android.internal.common.cacao.eip191 import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.android.internal.common.signing.cacao.toSignature -import com.walletconnect.android.internal.common.signing.eip191.EIP191Signer -import com.walletconnect.android.internal.common.signing.eip191.EIP191Verifier -import com.walletconnect.android.internal.common.signing.model.HexString -import com.walletconnect.android.internal.common.signing.signature.Signature -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.internal.common.signing.cacao.toSignature +import com.reown.android.internal.common.signing.eip191.EIP191Signer +import com.reown.android.internal.common.signing.eip191.EIP191Verifier +import com.reown.android.internal.common.signing.model.HexString +import com.reown.android.internal.common.signing.signature.Signature +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.android.utils.cacao.sign +import com.reown.util.bytesToHex +import com.reown.util.hexToBytes import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue import org.junit.Test diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt b/core/android/src/test/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt similarity index 96% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt rename to core/android/src/test/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt index ed5c17a62..cabeb846c 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/common/explorer/data/network/model/DappListingsDTOJunit4Test.kt @@ -1,4 +1,4 @@ -package com.walletconnect.android.internal.common.explorer.data.network.model +package com.reown.android.internal.common.explorer.data.network.model import androidx.core.net.toUri import com.squareup.moshi.Moshi diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/domain/RelayerInteractorTest.kt b/core/android/src/test/kotlin/com/reown/android/internal/domain/RelayerInteractorTest.kt similarity index 81% rename from core/android/src/test/kotlin/com/walletconnect/android/internal/domain/RelayerInteractorTest.kt rename to core/android/src/test/kotlin/com/reown/android/internal/domain/RelayerInteractorTest.kt index 58eef91ea..c263d59dc 100644 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/domain/RelayerInteractorTest.kt +++ b/core/android/src/test/kotlin/com/reown/android/internal/domain/RelayerInteractorTest.kt @@ -1,26 +1,27 @@ -package com.walletconnect.android.internal.domain - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.exception.WalletConnectException -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.domain.relay.RelayJsonRpcInteractor -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.model.type.Error -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.relay.RelayConnectionInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.foundation.network.model.RelayDTO -import com.walletconnect.foundation.util.Logger -import com.walletconnect.utils.Empty +package com.reown.android.internal.domain + +import com.reown.android.internal.common.ConditionalExponentialBackoffStrategy +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.exception.WalletConnectException +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.domain.relay.RelayJsonRpcInteractor +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.model.type.Error +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.relay.RelayConnectionInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.network.model.Relay +import com.reown.foundation.network.model.RelayDTO +import com.reown.foundation.util.Logger +import com.reown.utils.Empty import io.mockk.Called import io.mockk.every import io.mockk.mockk @@ -40,6 +41,8 @@ internal class RelayerInteractorTest { every { subscriptionRequest } returns flow { } } + private val backoffStrategy: ConditionalExponentialBackoffStrategy = mockk() + private val jsonRpcHistory: JsonRpcHistory = mockk { every { setRequest(any(), any(), any(), any(), any()) } returns true every { updateRequestWithResponse(any(), any()) } returns mockk() @@ -70,8 +73,9 @@ internal class RelayerInteractorTest { } private val sut = - spyk(RelayJsonRpcInteractor(relay, codec, jsonRpcHistory, pushMessagesRepository, logger), recordPrivateCalls = true) { - every { checkConnectionWorking() } answers { } + spyk(RelayJsonRpcInteractor(relay, codec, jsonRpcHistory, pushMessagesRepository, logger, backoffStrategy), recordPrivateCalls = true) { + every { checkNetworkConnectivity() } answers { } + every { relay.onResubscribe } returns flow { } } private val topicVO = Topic("mockkTopic") @@ -86,7 +90,7 @@ internal class RelayerInteractorTest { every { topic } returns topicVO } - val peerError: Error = mockk { + private val peerError: Error = mockk { every { message } returns "message" every { code } returns -1 } diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt b/core/android/src/test/kotlin/com/walletconnect/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt deleted file mode 100644 index f6510c357..000000000 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/JsonRpcResponseJsonRpcResultJsonAdapterTest.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.walletconnect.android.internal - -import com.squareup.moshi.Moshi -import com.tinder.scarlet.utils.getRawType -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.adapter.JsonRpcResultAdapter -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreAuthParams -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import org.junit.Test -import kotlin.reflect.jvm.jvmName - -internal class JsonRpcResponseJsonRpcResultJsonAdapterTest { - - @Test - fun `test sign params to json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - val approvalParams = - CoreSignParams.ApprovalParams(relay = RelayProtocolOptions("irn"), responderPublicKey = "124") - val jsonResult = JsonRpcResponse.JsonRpcResult( - id = 1L, - jsonrpc = "2.0", - result = approvalParams - ) - - val result = adapter.toJson(jsonResult) - println() - println(result) - println() - } - - @Test - fun `test sign params from json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - val approvalParamsJsonResult = JsonRpcResponse.JsonRpcResult( - id = 11L, - result = CoreSignParams.ApprovalParams(relay = RelayProtocolOptions("irn"), responderPublicKey = "124") - ) - val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(approvalParamsJsonResult) - val result = adapter.fromJson(resultString) - result is JsonRpcResponse.JsonRpcResult - println() - println(result) - println() - } - - @Test - fun `test auth params to json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - val authParams = CoreAuthParams.ResponseParams( - header = Cacao.Header("t"), - payload = Cacao.Payload("iss", "domain", "aud", "version", "nonce", "iat", "nbf", "exp", "statement", "id", listOf("res")), - signature = Cacao.Signature("t", "s") - ) - val jsonResult = JsonRpcResponse.JsonRpcResult( - id = 1L, - jsonrpc = "2.0", - result = authParams - ) - val result = adapter.toJson(jsonResult) - println() - println(result) - println() - } - - @Test - fun `test auth params from json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - - }.build() - - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - val authParams = CoreAuthParams.ResponseParams( - header = Cacao.Header("t"), - payload = Cacao.Payload("iss", "domain", "aud", "version", "nonce", "iat", "nbf", "exp", "statement", "id", listOf("res")), - signature = Cacao.Signature("t", "s") - ) - - val authParamsJsonResult = JsonRpcResponse.JsonRpcResult(id = 11L, result = authParams) - val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(authParamsJsonResult) - val result = adapter.fromJson(resultString) - result is JsonRpcResponse.JsonRpcResult - - println() - println(result) - println() - } - - @Test - fun `test chat params to json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - val chatParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = "did.jwt.auth") - val jsonResult = JsonRpcResponse.JsonRpcResult( - id = 1L, - jsonrpc = "2.0", - result = chatParams - ) - val result = adapter.toJson(jsonResult) - println() - println(result) - println() - } - - @Test - fun `test chat params from json`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val chatParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = "did.jwt.auth") - val authParamsJsonResult = JsonRpcResponse.JsonRpcResult(id = 11L, result = chatParams) - val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(authParamsJsonResult) - val result = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).fromJson(resultString) - result is JsonRpcResponse.JsonRpcResult - println() - println(result) - println() - } - - @Test - fun `test from json with boolean`() { - val moshi = Moshi.Builder().add { type, _, moshi -> - return@add if (type.getRawType().name == JsonRpcResponse.JsonRpcResult::class.jvmName) { - JsonRpcResultAdapter(moshi = moshi) - } else { - null - } - }.build() - val adapter = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java) - - val approvalParamsJsonResult = JsonRpcResponse.JsonRpcResult(id = 11L, result = true) - val resultString = moshi.adapter(JsonRpcResponse.JsonRpcResult::class.java).toJson(approvalParamsJsonResult) - - val result = adapter.fromJson(resultString) - result is JsonRpcResponse.JsonRpcResult - println() - println(result) - println() - } -} \ No newline at end of file diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/ValidatorTest.kt b/core/android/src/test/kotlin/com/walletconnect/android/internal/ValidatorTest.kt deleted file mode 100644 index dcf9dc7c0..000000000 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/ValidatorTest.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.walletconnect.android.internal - -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.WalletConnectUri -import com.walletconnect.foundation.common.model.Topic -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertNotNull -import junit.framework.TestCase.assertNull -import org.junit.Test - -internal class ValidatorTest { - - @Test - fun `validate with encoded uri query parameter`() { - val validUri = - "deeplink://wc?uri=wc%3A7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9%402%3Frelay-protocol%3Dirn%26symKey%3D587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - - Validator.validateWCUri("").apply { assertEquals(null, this) } - Validator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - } - } - - @Test - fun `validate with decoded uri query parameter`() { - val validUri = - "deeplink://wc?uri=wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - - Validator.validateWCUri("").apply { assertEquals(null, this) } - Validator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - } - } - - @Test - fun `validate WC uri test`() { - val validUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303&expiryTimestamp=1705667684" - - Validator.validateWCUri("").apply { assertEquals(null, this) } - Validator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - assertEquals(Expiry(1705667684L), expiry) - } - - val noTopicInvalidUri = - "wc:@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - Validator.validateWCUri(noTopicInvalidUri).apply { assertNull(this) } - - val noPrefixInvalidUri = - "7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - Validator.validateWCUri(noPrefixInvalidUri).apply { assertNull(this) } - - val noSymKeyInvalidUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=" - Validator.validateWCUri(noSymKeyInvalidUri).apply { assertNull(this) } - - val noProtocolTypeInvalidUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - Validator.validateWCUri(noProtocolTypeInvalidUri).apply { assertNull(this) } - } - - @Test - fun `validate WC uri test optional data field`() { - val validUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&relay-data=testData&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - - Validator.validateWCUri("").apply { assertEquals(null, this) } - Validator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("testData", this.relay.data) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - } - } - - @Test - fun `parse walletconnect uri to absolute string`() { - val uri = WalletConnectUri( - Topic("11112222244444"), - SymmetricKey("0x12321321312312312321"), - RelayProtocolOptions("irn", "teeestData"), - expiry = null, - methods = "wc_method" - ) - - assertEquals(uri.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&relay-data=teeestData&methods=wc_method&symKey=0x12321321312312312321") - - val uri2 = WalletConnectUri( - Topic("11112222244444"), - SymmetricKey("0x12321321312312312321"), - RelayProtocolOptions("irn"), - expiry = Expiry(1705667684), - methods = "" - ) - - assertEquals(uri2.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&expiryTimestamp=1705667684&symKey=0x12321321312312312321") - } -} \ No newline at end of file diff --git a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapterTest.kt b/core/android/src/test/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapterTest.kt deleted file mode 100644 index ef4bbf18d..000000000 --- a/core/android/src/test/kotlin/com/walletconnect/android/internal/common/adapter/ExpiryAdapterTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.android.internal.common.adapter - -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import junit.framework.TestCase.assertEquals -import org.junit.Test - -internal class ExpiryAdapterTest { - private val moshi = Moshi.Builder() - .add { _, _, _ -> - ExpiryAdapter - } - .add(KotlinJsonAdapterFactory()) - .build() - - @Test - fun toJson() { - val expiry = com.walletconnect.android.internal.common.model.Expiry(100L) - val expected = """"${expiry.seconds}"""" - - val expiryJson = moshi.adapter(com.walletconnect.android.internal.common.model.Expiry::class.java).toJson(expiry) - - assertEquals(expected, """"$expiryJson"""") - } -} \ No newline at end of file diff --git a/core/bom/build.gradle.kts b/core/bom/build.gradle.kts index 83a69ad08..c13a6119a 100644 --- a/core/bom/build.gradle.kts +++ b/core/bom/build.gradle.kts @@ -15,10 +15,8 @@ dependencies { api(project(":core:android")) api(project(":core:modal")) api(project(":protocol:sign")) - api(project(":protocol:auth")) api(project(":protocol:notify")) - api(project(":product:walletconnectmodal")) - api(project(":product:web3modal")) - api(project(":product:web3wallet")) + api(project(":product:appkit")) + api(project(":product:walletkit")) } } \ No newline at end of file diff --git a/core/modal/build.gradle.kts b/core/modal/build.gradle.kts index 1a5c518f8..c41252235 100644 --- a/core/modal/build.gradle.kts +++ b/core/modal/build.gradle.kts @@ -12,7 +12,7 @@ project.apply { } android { - namespace = "com.walletconnect.modalcore" + namespace = "com.reown.modalcore" compileSdk = COMPILE_SDK defaultConfig { @@ -76,5 +76,4 @@ dependencies { api(libs.bundles.androidxNavigation) implementation(libs.qrCodeGenerator) api(libs.timber) - } \ No newline at end of file diff --git a/core/modal/src/main/kotlin/com/reown/modal/ui/Preview.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/Preview.kt new file mode 100644 index 000000000..3b56bd1cf --- /dev/null +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/Preview.kt @@ -0,0 +1,18 @@ +package com.reown.modal.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +internal fun ComponentPreview( + color: Color = Color.White, + content: @Composable ColumnScope.() -> Unit +) { + Column(modifier = Modifier.background(color)) { + content() + } +} \ No newline at end of file diff --git a/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Images.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Images.kt new file mode 100644 index 000000000..b3e3387ce --- /dev/null +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Images.kt @@ -0,0 +1,26 @@ +package com.reown.modal.ui.components.common + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp + +@Composable +fun ClickableImage( + imageVector: ImageVector, + tint: Color, + modifier: Modifier = Modifier, + contentDescription: String? = null, + onClick: () -> Unit +) { + Image( + imageVector = imageVector, + colorFilter = ColorFilter.tint(tint), + contentDescription = contentDescription, + modifier = modifier.then(Modifier.roundedClickable(onClick = onClick).padding(4.dp)) + ) +} diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Modifiers.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Modifiers.kt similarity index 77% rename from core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Modifiers.kt rename to core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Modifiers.kt index 730c3540f..4afa7d7ca 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Modifiers.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Modifiers.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.ui.components.common +package com.reown.modal.ui.components.common import android.annotation.SuppressLint import androidx.compose.foundation.clickable @@ -17,7 +17,5 @@ fun Modifier.roundedClickable( onClick: () -> Unit ) = composed { clickable( enabled = enabled, - indication = rememberRipple(bounded = bounded, radius = radius), - interactionSource = remember { MutableInteractionSource() }, onClick = onClick ) } diff --git a/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Spacers.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Spacers.kt new file mode 100644 index 000000000..1f625ef24 --- /dev/null +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/components/common/Spacers.kt @@ -0,0 +1,30 @@ +package com.reown.modal.ui.components.common + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp + +@Composable +fun VerticalSpacer(height: Dp) { + Spacer(modifier = Modifier.height(height)) +} + +@Composable +fun HorizontalSpacer(width: Dp) { + Spacer(modifier = Modifier.width(width)) +} + +@Composable +fun RowScope.WeightSpacer(weight: Float = 1f) { + Spacer(modifier = Modifier.weight(weight)) +} + +@Composable +fun ColumnScope.WeightSpacer(weight: Float = 1f) { + Spacer(modifier = Modifier.weight(weight)) +} diff --git a/core/modal/src/main/kotlin/com/reown/modal/ui/components/logo/WalletConnectLogo.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/components/logo/WalletConnectLogo.kt new file mode 100644 index 000000000..64a07de41 --- /dev/null +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/components/logo/WalletConnectLogo.kt @@ -0,0 +1,51 @@ +package com.reown.modal.ui.components.logo + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Row +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.reown.modal.ui.ComponentPreview +import com.reown.modal.ui.components.common.HorizontalSpacer +import com.reown.modalcore.R + +@Composable +fun WalletConnectLogo(modifier: Modifier = Modifier, color: Color) { + Row( + modifier, + verticalAlignment = Alignment.CenterVertically + ) { + Image( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet_connect_logo), + contentDescription = "WalletConnectLogo", + colorFilter = ColorFilter.tint(color) + ) + HorizontalSpacer(width = 4.dp) + Text( + text = "WalletConnect", + style = TextStyle( + color = color, + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold + ) + ) + } +} + +@Preview +@Composable +private fun PreviewWalletConnectLogo() { + ComponentPreview { + WalletConnectLogo(color = Color.Blue) + } +} \ No newline at end of file diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/qr/WalletConnectQRCode.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/components/qr/WalletConnectQRCode.kt similarity index 96% rename from core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/qr/WalletConnectQRCode.kt rename to core/modal/src/main/kotlin/com/reown/modal/ui/components/qr/WalletConnectQRCode.kt index d99fd5981..2f4471415 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/qr/WalletConnectQRCode.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/components/qr/WalletConnectQRCode.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.ui.components.qr +package com.reown.modal.ui.components.qr import android.content.Context import android.graphics.Path @@ -25,8 +25,8 @@ import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorFrameSha import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorLogoPadding import com.github.alexzhirkevich.customqrgenerator.vector.style.QrVectorPixelShape import com.google.accompanist.drawablepainter.rememberDrawablePainter -import com.walletconnect.modal.ui.ComponentPreview -import com.walletconnect.modalcore.R +import com.reown.modal.ui.ComponentPreview +import com.reown.modalcore.R import androidx.compose.ui.graphics.Color as ComposeColor enum class QrCodeType { @@ -73,7 +73,7 @@ private fun WalletConnectModalQRCodePreview() { @Preview @Composable -private fun Web3ModalQRCodePreview() { +private fun AppKitQRCodePreview() { ComponentPreview { WalletConnectQRCode( "Preview qr code data with wallet connect logo inside", diff --git a/core/modal/src/main/kotlin/com/reown/modal/ui/model/UiState.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/model/UiState.kt new file mode 100644 index 000000000..0465e66ad --- /dev/null +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/model/UiState.kt @@ -0,0 +1,11 @@ +package com.reown.modal.ui.model + +sealed class UiState { + data class Success(val data: T) : UiState() + data class Loading(val data: T? = null, val loadingState: LoadingState = LoadingState.REFRESH) : UiState() + data class Error(val error: Throwable) : UiState() +} + +enum class LoadingState { + REFRESH, APPEND +} diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/search/SearchState.kt b/core/modal/src/main/kotlin/com/reown/modal/ui/model/search/SearchState.kt similarity index 95% rename from core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/search/SearchState.kt rename to core/modal/src/main/kotlin/com/reown/modal/ui/model/search/SearchState.kt index 7e763c49e..fa999f3fb 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/search/SearchState.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/ui/model/search/SearchState.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.ui.model.search +package com.reown.modal.ui.model.search import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/DeviceUtils.kt b/core/modal/src/main/kotlin/com/reown/modal/utils/DeviceUtils.kt similarity index 87% rename from core/modal/src/main/kotlin/com/walletconnect/modal/utils/DeviceUtils.kt rename to core/modal/src/main/kotlin/com/reown/modal/utils/DeviceUtils.kt index 5e23435da..7649dc4e8 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/DeviceUtils.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/utils/DeviceUtils.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.utils +package com.reown.modal.utils import android.content.res.Configuration import androidx.compose.runtime.Composable diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/UriExtensions.kt b/core/modal/src/main/kotlin/com/reown/modal/utils/UriExtensions.kt similarity index 97% rename from core/modal/src/main/kotlin/com/walletconnect/modal/utils/UriExtensions.kt rename to core/modal/src/main/kotlin/com/reown/modal/utils/UriExtensions.kt index e7f6fbb20..5b6d529ab 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/UriExtensions.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/utils/UriExtensions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.utils +package com.reown.modal.utils import androidx.compose.ui.platform.UriHandler import timber.log.Timber diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/theme/ColorsUtils.kt b/core/modal/src/main/kotlin/com/reown/modal/utils/theme/ColorsUtils.kt similarity index 92% rename from core/modal/src/main/kotlin/com/walletconnect/modal/utils/theme/ColorsUtils.kt rename to core/modal/src/main/kotlin/com/reown/modal/utils/theme/ColorsUtils.kt index 0be406db9..535cfa862 100644 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/utils/theme/ColorsUtils.kt +++ b/core/modal/src/main/kotlin/com/reown/modal/utils/theme/ColorsUtils.kt @@ -1,4 +1,4 @@ -package com.walletconnect.modal.utils.theme +package com.reown.modal.utils.theme import android.annotation.SuppressLint import android.content.Context diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/Preview.kt b/core/modal/src/main/kotlin/com/walletconnect/modal/ui/Preview.kt deleted file mode 100644 index b66c299b3..000000000 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/Preview.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.modal.ui - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color - -@Composable -internal fun ComponentPreview( - color: Color = Color.White, - content: @Composable ColumnScope.() -> Unit -) { - Column(modifier = Modifier.background(color)) { - content() - } -} \ No newline at end of file diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Images.kt b/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Images.kt deleted file mode 100644 index 1c8ae2894..000000000 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Images.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.modal.ui.components.common - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.dp - -@Composable -fun ClickableImage( - imageVector: ImageVector, - tint: Color, - modifier: Modifier = Modifier, - contentDescription: String? = null, - onClick: () -> Unit -) { - Image( - imageVector = imageVector, - colorFilter = ColorFilter.tint(tint), - contentDescription = contentDescription, - modifier = modifier.then(Modifier.roundedClickable(onClick = onClick).padding(4.dp)) - ) -} diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Spacers.kt b/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Spacers.kt deleted file mode 100644 index 4181e051e..000000000 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/common/Spacers.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.walletconnect.modal.ui.components.common - -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp - -@Composable -fun VerticalSpacer(height: Dp) { - Spacer(modifier = Modifier.height(height)) -} - -@Composable -fun HorizontalSpacer(width: Dp) { - Spacer(modifier = Modifier.width(width)) -} - -@Composable -fun RowScope.WeightSpacer(weight: Float = 1f) { - Spacer(modifier = Modifier.weight(weight)) -} - -@Composable -fun ColumnScope.WeightSpacer(weight: Float = 1f) { - Spacer(modifier = Modifier.weight(weight)) -} diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/logo/WalletConnectLogo.kt b/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/logo/WalletConnectLogo.kt deleted file mode 100644 index 400538cae..000000000 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/components/logo/WalletConnectLogo.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.walletconnect.modal.ui.components.logo - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Row -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.walletconnect.modal.ui.ComponentPreview -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.modalcore.R - -@Composable -fun WalletConnectLogo(modifier: Modifier = Modifier, color: Color) { - Row( - modifier, - verticalAlignment = Alignment.CenterVertically - ) { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet_connect_logo), - contentDescription = "WalletConnectLogo", - colorFilter = ColorFilter.tint(color) - ) - HorizontalSpacer(width = 4.dp) - Text( - text = "WalletConnect", - style = TextStyle( - color = color, - fontSize = 20.sp, - fontWeight = FontWeight.SemiBold - ) - ) - } -} - -@Preview -@Composable -private fun PreviewWalletConnectLogo() { - ComponentPreview { - WalletConnectLogo(color = Color.Blue) - } -} \ No newline at end of file diff --git a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/UiState.kt b/core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/UiState.kt deleted file mode 100644 index db6564d3a..000000000 --- a/core/modal/src/main/kotlin/com/walletconnect/modal/ui/model/UiState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.modal.ui.model - -sealed class UiState { - data class Success(val data: T) : UiState() - data class Loading(val data: T? = null, val loadingState: LoadingState = LoadingState.REFRESH) : UiState() - data class Error(val error: Throwable) : UiState() -} - -enum class LoadingState { - REFRESH, APPEND -} diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/RelayMapper.kt b/foundation/src/main/kotlin/com/reown/foundation/common/RelayMapper.kt similarity index 93% rename from foundation/src/main/kotlin/com/walletconnect/foundation/common/RelayMapper.kt rename to foundation/src/main/kotlin/com/reown/foundation/common/RelayMapper.kt index af6a3adc4..c2767495d 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/RelayMapper.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/common/RelayMapper.kt @@ -1,12 +1,12 @@ @file:JvmSynthetic -package com.walletconnect.foundation.common +package com.reown.foundation.common import com.tinder.scarlet.Message import com.tinder.scarlet.ShutdownReason import com.tinder.scarlet.WebSocket -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.foundation.network.model.RelayDTO +import com.reown.foundation.network.model.Relay +import com.reown.foundation.network.model.RelayDTO @JvmSynthetic fun WebSocket.Event.toRelayEvent() = when (this) { diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/SubscriptionIdAdapter.kt b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/SubscriptionIdAdapter.kt similarity index 88% rename from foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/SubscriptionIdAdapter.kt rename to foundation/src/main/kotlin/com/reown/foundation/common/adapters/SubscriptionIdAdapter.kt index 111e3ff95..97a14e38d 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/SubscriptionIdAdapter.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/SubscriptionIdAdapter.kt @@ -1,7 +1,7 @@ -package com.walletconnect.foundation.common.adapters +package com.reown.foundation.common.adapters import com.squareup.moshi.* -import com.walletconnect.foundation.common.model.SubscriptionId +import com.reown.foundation.common.model.SubscriptionId internal object SubscriptionIdAdapter: JsonAdapter() { diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TopicAdapter.kt b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/TopicAdapter.kt similarity index 88% rename from foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TopicAdapter.kt rename to foundation/src/main/kotlin/com/reown/foundation/common/adapters/TopicAdapter.kt index e55c6cb6c..5e1bb82dc 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TopicAdapter.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/TopicAdapter.kt @@ -1,7 +1,7 @@ -package com.walletconnect.foundation.common.adapters +package com.reown.foundation.common.adapters import com.squareup.moshi.* -import com.walletconnect.foundation.common.model.Topic +import com.reown.foundation.common.model.Topic internal object TopicAdapter: JsonAdapter() { diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TtlAdapter.kt b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/TtlAdapter.kt similarity index 88% rename from foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TtlAdapter.kt rename to foundation/src/main/kotlin/com/reown/foundation/common/adapters/TtlAdapter.kt index b16310add..bc30f56b5 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/adapters/TtlAdapter.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/common/adapters/TtlAdapter.kt @@ -1,7 +1,7 @@ -package com.walletconnect.foundation.common.adapters +package com.reown.foundation.common.adapters import com.squareup.moshi.* -import com.walletconnect.foundation.common.model.Ttl +import com.reown.foundation.common.model.Ttl internal object TtlAdapter : JsonAdapter() { diff --git a/foundation/src/main/kotlin/com/reown/foundation/common/model/Key.kt b/foundation/src/main/kotlin/com/reown/foundation/common/model/Key.kt new file mode 100644 index 000000000..464932f7d --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/common/model/Key.kt @@ -0,0 +1,15 @@ +package com.reown.foundation.common.model + +import com.reown.util.hexToBytes + +interface Key { + val keyAsHex: String + val keyAsBytes: ByteArray + get() = keyAsHex.hexToBytes() +} + +@JvmInline +value class PublicKey(override val keyAsHex: String) : Key + +@JvmInline +value class PrivateKey(override val keyAsHex: String) : Key \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/common/model/SubscriptionId.kt b/foundation/src/main/kotlin/com/reown/foundation/common/model/SubscriptionId.kt new file mode 100644 index 000000000..88ac264e5 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/common/model/SubscriptionId.kt @@ -0,0 +1,6 @@ +package com.reown.foundation.common.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = false) +data class SubscriptionId(val id: String) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/common/model/Topic.kt b/foundation/src/main/kotlin/com/reown/foundation/common/model/Topic.kt new file mode 100644 index 000000000..47d5b2434 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/common/model/Topic.kt @@ -0,0 +1,7 @@ +package com.reown.foundation.common.model + +import com.squareup.moshi.JsonClass +import com.reown.util.Empty + +@JsonClass(generateAdapter = false) +data class Topic(val value: String = String.Empty) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/common/model/Ttl.kt b/foundation/src/main/kotlin/com/reown/foundation/common/model/Ttl.kt new file mode 100644 index 000000000..0f2956056 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/common/model/Ttl.kt @@ -0,0 +1,6 @@ +package com.reown.foundation.common.model + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = false) +data class Ttl(val seconds: Long) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt similarity index 77% rename from foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt rename to foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt index 663c01ee0..cf08d940e 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/BaseClientIdJwtRepository.kt @@ -1,17 +1,15 @@ -package com.walletconnect.foundation.crypto.data.repository +package com.reown.foundation.crypto.data.repository -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.crypto.data.repository.model.IrnJwtClaims -import com.walletconnect.foundation.util.jwt.JwtHeader -import com.walletconnect.foundation.util.jwt.encodeData -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey -import com.walletconnect.foundation.util.jwt.encodeJWT -import com.walletconnect.foundation.util.jwt.jwtIatAndExp -import com.walletconnect.foundation.util.jwt.signJwt -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.hexToBytes -import com.walletconnect.util.randomBytes +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.crypto.data.repository.model.IrnJwtClaims +import com.reown.foundation.util.jwt.JwtHeader +import com.reown.foundation.util.jwt.encodeData +import com.reown.foundation.util.jwt.encodeEd25519DidKey +import com.reown.foundation.util.jwt.encodeJWT +import com.reown.foundation.util.jwt.jwtIatAndExp +import com.reown.foundation.util.jwt.signJwt +import com.reown.util.* import org.bouncycastle.crypto.AsymmetricCipherKeyPair import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters diff --git a/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepository.kt b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepository.kt new file mode 100644 index 000000000..7cdf038d8 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepository.kt @@ -0,0 +1,6 @@ +package com.reown.foundation.crypto.data.repository + +interface ClientIdJwtRepository { + + fun generateJWT(serverUrl: String, getIssuerClientId: (String) -> Unit = {}): String +} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/model/IrnJwtClaims.kt b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/model/IrnJwtClaims.kt similarity index 75% rename from foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/model/IrnJwtClaims.kt rename to foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/model/IrnJwtClaims.kt index d2c4bca22..01cd2ae36 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/model/IrnJwtClaims.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/crypto/data/repository/model/IrnJwtClaims.kt @@ -1,8 +1,8 @@ -package com.walletconnect.foundation.crypto.data.repository.model +package com.reown.foundation.crypto.data.repository.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.foundation.util.jwt.JwtClaims +import com.reown.foundation.util.jwt.JwtClaims @JsonClass(generateAdapter = true) data class IrnJwtClaims( diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCommonModule.kt b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationCommonModule.kt similarity index 75% rename from foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCommonModule.kt rename to foundation/src/main/kotlin/com/reown/foundation/di/FoundationCommonModule.kt index 65bc2db19..56eac095d 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCommonModule.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationCommonModule.kt @@ -1,15 +1,15 @@ -package com.walletconnect.foundation.di +package com.reown.foundation.di import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.tinder.scarlet.utils.getRawType -import com.walletconnect.foundation.common.adapters.SubscriptionIdAdapter -import com.walletconnect.foundation.common.adapters.TopicAdapter -import com.walletconnect.foundation.common.adapters.TtlAdapter -import com.walletconnect.foundation.common.model.SubscriptionId -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger +import com.reown.foundation.common.adapters.SubscriptionIdAdapter +import com.reown.foundation.common.adapters.TopicAdapter +import com.reown.foundation.common.adapters.TtlAdapter +import com.reown.foundation.common.model.SubscriptionId +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger import org.koin.core.qualifier.named import org.koin.dsl.module import kotlin.reflect.jvm.jvmName diff --git a/foundation/src/main/kotlin/com/reown/foundation/di/FoundationCryptoModule.kt b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationCryptoModule.kt new file mode 100644 index 000000000..b2c7296f7 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationCryptoModule.kt @@ -0,0 +1,18 @@ +@file:JvmSynthetic + +package com.reown.foundation.di + +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.crypto.data.repository.BaseClientIdJwtRepository +import com.reown.foundation.crypto.data.repository.ClientIdJwtRepository +import org.koin.dsl.module + +internal fun cryptoModule() = module { + + single { + object: BaseClientIdJwtRepository() { + override fun setKeyPair(key: String, privateKey: PrivateKey, publicKey: PublicKey) {} + } + } +} diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationDITags.kt b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationDITags.kt similarity index 78% rename from foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationDITags.kt rename to foundation/src/main/kotlin/com/reown/foundation/di/FoundationDITags.kt index 808a69c78..4b80ff8d9 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationDITags.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationDITags.kt @@ -1,4 +1,4 @@ -package com.walletconnect.foundation.di +package com.reown.foundation.di enum class FoundationDITags { MOSHI, INTERCEPTOR, OK_HTTP, RELAY_SERVICE, SCARLET, MSG_ADAPTER, MANUAL_CONNECTION_CONTROLLER, MANUAL_LIFECYCLE diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationNetworkModule.kt b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt similarity index 91% rename from foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationNetworkModule.kt rename to foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt index 88070e2e6..0ed01af79 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationNetworkModule.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt @@ -1,12 +1,12 @@ -package com.walletconnect.foundation.di +package com.reown.foundation.di import com.tinder.scarlet.Scarlet import com.tinder.scarlet.messageadapter.moshi.MoshiMessageAdapter import com.tinder.scarlet.retry.LinearBackoffStrategy import com.tinder.scarlet.websocket.okhttp.newWebSocketFactory -import com.walletconnect.foundation.network.BaseRelayClient -import com.walletconnect.foundation.network.data.adapter.FlowStreamAdapter -import com.walletconnect.foundation.network.data.service.RelayService +import com.reown.foundation.network.BaseRelayClient +import com.reown.foundation.network.data.adapter.FlowStreamAdapter +import com.reown.foundation.network.data.service.RelayService import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor diff --git a/foundation/src/main/kotlin/com/reown/foundation/network/BaseRelayClient.kt b/foundation/src/main/kotlin/com/reown/foundation/network/BaseRelayClient.kt new file mode 100644 index 000000000..79fca2a77 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/network/BaseRelayClient.kt @@ -0,0 +1,400 @@ +package com.reown.foundation.network + +import com.tinder.scarlet.WebSocket +import com.reown.foundation.common.model.SubscriptionId +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.common.toRelay +import com.reown.foundation.common.toRelayEvent +import com.reown.foundation.di.foundationCommonModule +import com.reown.foundation.network.data.service.RelayService +import com.reown.foundation.network.model.Relay +import com.reown.foundation.network.model.RelayDTO +import com.reown.foundation.util.Logger +import com.reown.foundation.util.scope +import com.reown.util.generateClientToServerId +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.job +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeout +import org.koin.core.KoinApplication + +sealed class ConnectionState { + data object Open : ConnectionState() + data class Closed(val throwable: Throwable) : ConnectionState() + data object Idle : ConnectionState() +} + +@OptIn(ExperimentalCoroutinesApi::class) +abstract class BaseRelayClient : RelayInterface { + private var foundationKoinApp: KoinApplication = KoinApplication.init() + lateinit var relayService: RelayService + lateinit var connectionLifecycle: ConnectionLifecycle + protected var logger: Logger + private val resultState: MutableSharedFlow = MutableSharedFlow() + internal var connectionState: MutableStateFlow = MutableStateFlow(ConnectionState.Idle) + private var unAckedTopics: MutableList = mutableListOf() + private var isConnecting: Boolean = false + private var retryCount: Int = 0 + override var isLoggingEnabled: Boolean = false + + init { + foundationKoinApp.run { modules(foundationCommonModule()) } + logger = foundationKoinApp.koin.get() + } + + fun observeResults() { + scope.launch { + merge( + relayService.observePublishAcknowledgement(), + relayService.observePublishError(), + relayService.observeBatchSubscribeAcknowledgement(), + relayService.observeBatchSubscribeError(), + relayService.observeSubscribeAcknowledgement(), + relayService.observeSubscribeError(), + relayService.observeUnsubscribeAcknowledgement(), + relayService.observeUnsubscribeError() + ) + .catch { exception -> logger.error(exception) } + .collect { result -> + if (isLoggingEnabled) { + println("Result: $result; timestamp: ${System.currentTimeMillis()}") + } + + resultState.emit(result) + } + } + } + + override val eventsFlow: SharedFlow by lazy { + relayService + .observeWebSocketEvent() + .onEach { event -> + if (event is WebSocket.Event.OnConnectionOpened<*>) { + connectionState.value = ConnectionState.Open + } else if (event is WebSocket.Event.OnConnectionClosed || event is WebSocket.Event.OnConnectionFailed) { + connectionState.value = ConnectionState.Closed(getError(event)) + } + } + .map { event -> + logger.log("Event: $event") + event.toRelayEvent() + } + .shareIn(scope, SharingStarted.Lazily, REPLAY) + } + + override val subscriptionRequest: Flow by lazy { + relayService.observeSubscriptionRequest() + .map { request -> request.toRelay() } + .onEach { relayRequest -> supervisorScope { publishSubscriptionAcknowledgement(relayRequest.id) } } + } + + @ExperimentalCoroutinesApi + override fun publish( + topic: String, + message: String, + params: Relay.Model.IrnParams, + id: Long?, + onResult: (Result) -> Unit, + ) { + connectAndCallRelay( + onConnected = { + val (tag, ttl, prompt) = params + val publishParams = RelayDTO.Publish.Request.Params(Topic(topic), message, Ttl(ttl), tag, prompt) + val publishRequest = RelayDTO.Publish.Request(id = id ?: generateClientToServerId(), params = publishParams) + observePublishResult(publishRequest.id, onResult) + relayService.publishRequest(publishRequest) + }, + onFailure = { onResult(Result.failure(it)) } + ) + } + + private fun observePublishResult(id: Long, onResult: (Result) -> Unit) { + scope.launch { + try { + withTimeout(RESULT_TIMEOUT) { + resultState + .filterIsInstance() + .filter { relayResult -> relayResult.id == id } + .first { publishResult -> + when (publishResult) { + is RelayDTO.Publish.Result.Acknowledgement -> onResult(Result.success(publishResult.toRelay())) + is RelayDTO.Publish.Result.JsonRpcError -> onResult(Result.failure(Throwable(publishResult.error.errorMessage))) + } + true + } + } + } catch (e: TimeoutCancellationException) { + onResult(Result.failure(e)) + cancelJobIfActive() + } catch (e: Exception) { + onResult(Result.failure(e)) + cancelJobIfActive() + } + } + } + + @ExperimentalCoroutinesApi + override fun subscribe(topic: String, id: Long?, onResult: (Result) -> Unit) { + connectAndCallRelay( + onConnected = { + val subscribeRequest = RelayDTO.Subscribe.Request(id = id ?: generateClientToServerId(), params = RelayDTO.Subscribe.Request.Params(Topic(topic))) + if (isLoggingEnabled) { + logger.log("Sending SubscribeRequest: $subscribeRequest; timestamp: ${System.currentTimeMillis()}") + } + observeSubscribeResult(subscribeRequest.id, onResult) + relayService.subscribeRequest(subscribeRequest) + }, + onFailure = { onResult(Result.failure(it)) } + ) + } + + private fun observeSubscribeResult(id: Long, onResult: (Result) -> Unit) { + scope.launch { + try { + withTimeout(RESULT_TIMEOUT) { + if (isLoggingEnabled) println("ObserveSubscribeResult: $id; timestamp: ${System.currentTimeMillis()}") + resultState + .onEach { relayResult -> if (isLoggingEnabled) logger.log("SubscribeResult 1: $relayResult") } + .filterIsInstance() + .onEach { relayResult -> if (isLoggingEnabled) logger.log("SubscribeResult 2: $relayResult") } + .filter { relayResult -> relayResult.id == id } + .first { subscribeResult -> + if (isLoggingEnabled) println("SubscribeResult 3: $subscribeResult") + when (subscribeResult) { + is RelayDTO.Subscribe.Result.Acknowledgement -> onResult(Result.success(subscribeResult.toRelay())) + is RelayDTO.Subscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(subscribeResult.error.errorMessage))) + } + true + } + } + } catch (e: TimeoutCancellationException) { + onResult(Result.failure(e)) + cancelJobIfActive() + } catch (e: Exception) { + onResult(Result.failure(e)) + cancelJobIfActive() + } + } + } + + @ExperimentalCoroutinesApi + override fun batchSubscribe(topics: List, id: Long?, onResult: (Result) -> Unit) { + connectAndCallRelay( + onConnected = { + if (!unAckedTopics.containsAll(topics)) { + unAckedTopics.addAll(topics) + val batchSubscribeRequest = RelayDTO.BatchSubscribe.Request(id = id ?: generateClientToServerId(), params = RelayDTO.BatchSubscribe.Request.Params(topics)) + observeBatchSubscribeResult(batchSubscribeRequest.id, topics, onResult) + relayService.batchSubscribeRequest(batchSubscribeRequest) + } + }, + onFailure = { onResult(Result.failure(it)) } + ) + } + + private fun observeBatchSubscribeResult(id: Long, topics: List, onResult: (Result) -> Unit) { + scope.launch { + try { + withTimeout(RESULT_TIMEOUT) { + resultState + .filterIsInstance() + .onEach { if (unAckedTopics.isNotEmpty()) unAckedTopics.removeAll(topics) } + .filter { relayResult -> relayResult.id == id } + .first { batchSubscribeResult -> + when (batchSubscribeResult) { + is RelayDTO.BatchSubscribe.Result.Acknowledgement -> onResult(Result.success(batchSubscribeResult.toRelay())) + is RelayDTO.BatchSubscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(batchSubscribeResult.error.errorMessage))) + } + true + } + } + } catch (e: TimeoutCancellationException) { + onResult(Result.failure(e)) + cancelJobIfActive() + } catch (e: Exception) { + onResult(Result.failure(e)) + cancelJobIfActive() + } + } + } + + @ExperimentalCoroutinesApi + override fun unsubscribe( + topic: String, + subscriptionId: String, + id: Long?, + onResult: (Result) -> Unit, + ) { + connectAndCallRelay( + onConnected = { + val unsubscribeRequest = RelayDTO.Unsubscribe.Request( + id = id ?: generateClientToServerId(), + params = RelayDTO.Unsubscribe.Request.Params(Topic(topic), SubscriptionId(subscriptionId)) + ) + + observeUnsubscribeResult(unsubscribeRequest.id, onResult) + relayService.unsubscribeRequest(unsubscribeRequest) + }, + onFailure = { onResult(Result.failure(it)) } + ) + } + + private fun observeUnsubscribeResult(id: Long, onResult: (Result) -> Unit) { + scope.launch { + try { + withTimeout(RESULT_TIMEOUT) { + resultState + .filterIsInstance() + .filter { relayResult -> relayResult.id == id } + .first { unsubscribeResult -> + when (unsubscribeResult) { + is RelayDTO.Unsubscribe.Result.Acknowledgement -> onResult(Result.success(unsubscribeResult.toRelay())) + is RelayDTO.Unsubscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(unsubscribeResult.error.errorMessage))) + } + true + } + } + } catch (e: TimeoutCancellationException) { + onResult(Result.failure(e)) + cancelJobIfActive() + } catch (e: Exception) { + onResult(Result.failure(e)) + cancelJobIfActive() + } + } + } + + private fun connectAndCallRelay(onConnected: () -> Unit, onFailure: (Throwable) -> Unit) { + when { + shouldConnect() -> connect(onConnected, onFailure) + isConnecting -> awaitConnection(onConnected, onFailure) + connectionState.value == ConnectionState.Open -> onConnected() + } + } + + private fun shouldConnect() = !isConnecting && (connectionState.value is ConnectionState.Closed || connectionState.value is ConnectionState.Idle) + private fun connect(onConnected: () -> Unit, onFailure: (Throwable) -> Unit) { + isConnecting = true + connectionLifecycle.reconnect() + awaitConnectionWithRetry( + onConnected = { + reset() + onConnected() + }, + onFailure = { error -> + reset() + onFailure(error) + } + ) + } + + private fun awaitConnectionWithRetry(onConnected: () -> Unit, onFailure: (Throwable) -> Unit = {}) { + scope.launch { + try { + withTimeout(CONNECTION_TIMEOUT) { + connectionState + .filter { state -> state != ConnectionState.Idle } + .take(4) + .onEach { state -> handleRetries(state, onFailure) } + .filter { state -> state == ConnectionState.Open } + .firstOrNull { + onConnected() + true + } + } + } catch (e: TimeoutCancellationException) { + onFailure(e) + cancelJobIfActive() + } catch (e: Exception) { + if (e !is CancellationException) { + onFailure(e) + } + } + } + } + + private fun awaitConnection(onConnected: () -> Unit, onFailure: (Throwable) -> Unit) { + scope.launch { + try { + withTimeout(CONNECTION_TIMEOUT) { + connectionState + .filter { state -> state is ConnectionState.Open } + .firstOrNull { + onConnected() + true + } + } + } catch (e: TimeoutCancellationException) { + onFailure(e) + cancelJobIfActive() + } catch (e: Exception) { + if (e !is CancellationException) { + onFailure(e) + } + cancelJobIfActive() + } + } + } + + private fun CoroutineScope.handleRetries(state: ConnectionState, onFailure: (Throwable) -> Unit) { + if (state is ConnectionState.Closed) { + if (retryCount == MAX_RETRIES) { + onFailure(Throwable("Connectivity error, please check your Internet connection and try again")) + cancelJobIfActive() + } else { + connectionLifecycle.reconnect() + retryCount++ + } + } + } + + private fun getError(event: WebSocket.Event): Throwable = when (event) { + is WebSocket.Event.OnConnectionClosed -> Throwable(event.shutdownReason.reason) + is WebSocket.Event.OnConnectionFailed -> event.throwable + else -> Throwable("Unknown") + } + + private fun publishSubscriptionAcknowledgement(id: Long) { + val publishRequest = RelayDTO.Subscription.Result.Acknowledgement(id = id, result = true) + relayService.publishSubscriptionAcknowledgement(publishRequest) + } + + private fun CoroutineScope.cancelJobIfActive() { + if (this.coroutineContext.job.isActive) { + this.coroutineContext.job.cancel() + } + } + + private fun reset() { + isConnecting = false + retryCount = 0 + } + + private companion object { + const val REPLAY: Int = 1 + const val RESULT_TIMEOUT: Long = 60000 + const val CONNECTION_TIMEOUT: Long = 15000 + const val MAX_RETRIES: Int = 3 + } +} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/network/ConnectionLifecycle.kt b/foundation/src/main/kotlin/com/reown/foundation/network/ConnectionLifecycle.kt new file mode 100644 index 000000000..6c1a16045 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/network/ConnectionLifecycle.kt @@ -0,0 +1,8 @@ +package com.reown.foundation.network + +import kotlinx.coroutines.flow.StateFlow + +interface ConnectionLifecycle { + val onResume: StateFlow + fun reconnect() +} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/RelayInterface.kt b/foundation/src/main/kotlin/com/reown/foundation/network/RelayInterface.kt similarity index 90% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/RelayInterface.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/RelayInterface.kt index 1e3638c5a..ed91f69f6 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/RelayInterface.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/RelayInterface.kt @@ -1,6 +1,6 @@ -package com.walletconnect.foundation.network +package com.reown.foundation.network -import com.walletconnect.foundation.network.model.Relay +import com.reown.foundation.network.model.Relay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/data/adapter/FlowStreamAdapter.kt b/foundation/src/main/kotlin/com/reown/foundation/network/data/adapter/FlowStreamAdapter.kt similarity index 92% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/data/adapter/FlowStreamAdapter.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/data/adapter/FlowStreamAdapter.kt index cd81d91ad..34bca919e 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/data/adapter/FlowStreamAdapter.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/data/adapter/FlowStreamAdapter.kt @@ -1,4 +1,4 @@ -package com.walletconnect.foundation.network.data.adapter +package com.reown.foundation.network.data.adapter import com.tinder.scarlet.Stream import com.tinder.scarlet.StreamAdapter diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/data/service/RelayService.kt b/foundation/src/main/kotlin/com/reown/foundation/network/data/service/RelayService.kt similarity index 93% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/data/service/RelayService.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/data/service/RelayService.kt index 62def0bcc..17f826e6b 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/data/service/RelayService.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/data/service/RelayService.kt @@ -1,9 +1,9 @@ -package com.walletconnect.foundation.network.data.service +package com.reown.foundation.network.data.service import com.tinder.scarlet.WebSocket import com.tinder.scarlet.ws.Receive import com.tinder.scarlet.ws.Send -import com.walletconnect.foundation.network.model.RelayDTO +import com.reown.foundation.network.model.RelayDTO import kotlinx.coroutines.flow.Flow interface RelayService { diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/JsonRpcRelay.kt b/foundation/src/main/kotlin/com/reown/foundation/network/model/JsonRpcRelay.kt similarity index 88% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/model/JsonRpcRelay.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/model/JsonRpcRelay.kt index f15ab606f..7ad8cc408 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/JsonRpcRelay.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/model/JsonRpcRelay.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.foundation.network.model +package com.reown.foundation.network.model private const val SUFFIX = "irn" internal const val IRN_PUBLISH: String = "${SUFFIX}_publish" diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/Relay.kt b/foundation/src/main/kotlin/com/reown/foundation/network/model/Relay.kt similarity index 99% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/model/Relay.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/model/Relay.kt index e329e852e..e0d6ab0fb 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/Relay.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/model/Relay.kt @@ -1,4 +1,4 @@ -package com.walletconnect.foundation.network.model +package com.reown.foundation.network.model object Relay { diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/RelayDTO.kt b/foundation/src/main/kotlin/com/reown/foundation/network/model/RelayDTO.kt similarity index 94% rename from foundation/src/main/kotlin/com/walletconnect/foundation/network/model/RelayDTO.kt rename to foundation/src/main/kotlin/com/reown/foundation/network/model/RelayDTO.kt index 234090742..f1ccc0938 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/model/RelayDTO.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/network/model/RelayDTO.kt @@ -1,14 +1,14 @@ -package com.walletconnect.foundation.network.model +package com.reown.foundation.network.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.foundation.common.adapters.SubscriptionIdAdapter -import com.walletconnect.foundation.common.adapters.TopicAdapter -import com.walletconnect.foundation.common.adapters.TtlAdapter -import com.walletconnect.foundation.common.model.SubscriptionId -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.util.generateClientToServerId +import com.reown.foundation.common.adapters.SubscriptionIdAdapter +import com.reown.foundation.common.adapters.TopicAdapter +import com.reown.foundation.common.adapters.TtlAdapter +import com.reown.foundation.common.model.SubscriptionId +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.util.generateClientToServerId sealed class RelayDTO { abstract val id: Long diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/Logger.kt b/foundation/src/main/kotlin/com/reown/foundation/util/Logger.kt similarity index 79% rename from foundation/src/main/kotlin/com/walletconnect/foundation/util/Logger.kt rename to foundation/src/main/kotlin/com/reown/foundation/util/Logger.kt index 72072b636..163539f7c 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/Logger.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/util/Logger.kt @@ -1,4 +1,4 @@ -package com.walletconnect.foundation.util +package com.reown.foundation.util interface Logger { diff --git a/foundation/src/main/kotlin/com/reown/foundation/util/Scope.kt b/foundation/src/main/kotlin/com/reown/foundation/util/Scope.kt new file mode 100644 index 000000000..c932bd18f --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/util/Scope.kt @@ -0,0 +1,8 @@ +package com.reown.foundation.util + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +internal val job = SupervisorJob() +internal var scope = CoroutineScope(job + Dispatchers.IO) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/util/UtilFunctions.kt b/foundation/src/main/kotlin/com/reown/foundation/util/UtilFunctions.kt new file mode 100644 index 000000000..91a6f4182 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/util/UtilFunctions.kt @@ -0,0 +1,62 @@ +@file:Suppress("PackageDirectoryMismatch") + +package com.reown.util + +import java.security.SecureRandom +import kotlin.math.pow + +@get:JvmSynthetic +val String.Companion.Empty + get() = "" + +fun generateId(): Long = ("${System.currentTimeMillis()}${(100..999).random()}").toLong() + +fun generateClientToServerId(): Long { + val now = System.currentTimeMillis() * 10.0.pow(6.0) + return now.plus((100000..999999).random()).toLong() +} + +fun randomBytes(size: Int): ByteArray = ByteArray(size).apply { + SecureRandom().nextBytes(this) +} + +fun ByteArray.bytesToHex(): String { + val hexString = StringBuilder(2 * this.size) + + this.indices.forEach { i -> + val hex = Integer.toHexString(0xff and this[i].toInt()) + + if (hex.length == 1) { + hexString.append('0') + } + + hexString.append(hex) + } + + return hexString.toString() +} + +fun ByteArray.bytesToInt(size: Int): Int { + require(this.size <= 4) { "Byte array size must be less than 5" } + + return (0 until size).fold(0) { acc, i -> + acc.or(this[i].toInt().shl((size - 1 - i) * 8)) + } +} + +fun String.hexToBytes(): ByteArray { + val len = this.length + val data = ByteArray(len / 2) + var i = 0 + + while (i < len) { + data[i / 2] = ((Character.digit(this[i], 16) shl 4) + + Character.digit(this[i + 1], 16)).toByte() + i += 2 + } + + return data +} + +@JvmSynthetic +internal fun String.addUserAgent(sdkVersion: String): String = "$this&ua=wc-2/kotlin-$sdkVersion" \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtClaims.kt b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtClaims.kt new file mode 100644 index 000000000..ef11a1488 --- /dev/null +++ b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtClaims.kt @@ -0,0 +1,5 @@ +package com.reown.foundation.util.jwt + +interface JwtClaims { + val issuer: String +} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtHeader.kt b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtHeader.kt similarity index 90% rename from foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtHeader.kt rename to foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtHeader.kt index dca35ff3d..942bbd261 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtHeader.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtHeader.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.foundation.util.jwt +package com.reown.foundation.util.jwt import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtUtils.kt b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtUtils.kt similarity index 96% rename from foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtUtils.kt rename to foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtUtils.kt index 19331a6af..e49ca3667 100644 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtUtils.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/util/jwt/JwtUtils.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.foundation.util.jwt +package com.reown.foundation.util.jwt import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.util.bytesToHex +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.util.bytesToHex import io.ipfs.multibase.Base58 import io.ipfs.multibase.Multibase import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Key.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Key.kt deleted file mode 100644 index 52c55ed8c..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Key.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.foundation.common.model - -import com.walletconnect.util.hexToBytes - -interface Key { - val keyAsHex: String - val keyAsBytes: ByteArray - get() = keyAsHex.hexToBytes() -} - -@JvmInline -value class PublicKey(override val keyAsHex: String) : Key - -@JvmInline -value class PrivateKey(override val keyAsHex: String) : Key \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/SubscriptionId.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/SubscriptionId.kt deleted file mode 100644 index 5ebb1d509..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/SubscriptionId.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.foundation.common.model - -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = false) -data class SubscriptionId(val id: String) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Topic.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Topic.kt deleted file mode 100644 index 1e5b76088..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Topic.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.foundation.common.model - -import com.squareup.moshi.JsonClass -import com.walletconnect.util.Empty - -@JsonClass(generateAdapter = false) -data class Topic(val value: String = String.Empty) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Ttl.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Ttl.kt deleted file mode 100644 index db9120d34..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/common/model/Ttl.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.foundation.common.model - -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = false) -data class Ttl(val seconds: Long) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepository.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepository.kt deleted file mode 100644 index 7d015bd3a..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepository.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.foundation.crypto.data.repository - -interface ClientIdJwtRepository { - - fun generateJWT(serverUrl: String, getIssuerClientId: (String) -> Unit = {}): String -} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCryptoModule.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCryptoModule.kt deleted file mode 100644 index 9d0fe483f..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/di/FoundationCryptoModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.foundation.di - -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.crypto.data.repository.BaseClientIdJwtRepository -import com.walletconnect.foundation.crypto.data.repository.ClientIdJwtRepository -import org.koin.dsl.module - -internal fun cryptoModule() = module { - - single { - object: BaseClientIdJwtRepository() { - override fun setKeyPair(key: String, privateKey: PrivateKey, publicKey: PublicKey) {} - } - } -} diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/network/BaseRelayClient.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/network/BaseRelayClient.kt deleted file mode 100644 index f8289b3af..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/network/BaseRelayClient.kt +++ /dev/null @@ -1,262 +0,0 @@ -package com.walletconnect.foundation.network - -import com.walletconnect.foundation.common.model.SubscriptionId -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.common.toRelay -import com.walletconnect.foundation.common.toRelayEvent -import com.walletconnect.foundation.di.foundationCommonModule -import com.walletconnect.foundation.network.data.service.RelayService -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.foundation.network.model.RelayDTO -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.scope -import com.walletconnect.util.generateClientToServerId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.job -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import org.koin.core.KoinApplication - -@OptIn(ExperimentalCoroutinesApi::class) -abstract class BaseRelayClient : RelayInterface { - private var foundationKoinApp: KoinApplication = KoinApplication.init() - lateinit var relayService: RelayService - protected var logger: Logger - private val resultState: MutableSharedFlow = MutableSharedFlow() - override var isLoggingEnabled: Boolean = false - - init { - foundationKoinApp.run { modules(foundationCommonModule()) } - logger = foundationKoinApp.koin.get() - } - - fun observeResults() { - scope.launch { - merge( - relayService.observePublishAcknowledgement(), - relayService.observePublishError(), - relayService.observeBatchSubscribeAcknowledgement(), - relayService.observeBatchSubscribeError(), - relayService.observeSubscribeAcknowledgement(), - relayService.observeSubscribeError(), - relayService.observeUnsubscribeAcknowledgement(), - relayService.observeUnsubscribeError() - ) - .catch { exception -> logger.error(exception) } - .collect { result -> - if (isLoggingEnabled) { - println("Result: $result; timestamp: ${System.currentTimeMillis()}") - } - - resultState.emit(result) - } - } - } - - override val eventsFlow: SharedFlow by lazy { - relayService - .observeWebSocketEvent() - .map { event -> - if (isLoggingEnabled) { - println("Event: $event") - } - event.toRelayEvent() - } - .shareIn(scope, SharingStarted.Lazily, REPLAY) - } - - override val subscriptionRequest: Flow by lazy { - relayService.observeSubscriptionRequest() - .map { request -> request.toRelay() } - .onEach { relayRequest -> supervisorScope { publishSubscriptionAcknowledgement(relayRequest.id) } } - } - - @ExperimentalCoroutinesApi - override fun publish( - topic: String, - message: String, - params: Relay.Model.IrnParams, - id: Long?, - onResult: (Result) -> Unit, - ) { - val (tag, ttl, prompt) = params - val publishParams = RelayDTO.Publish.Request.Params(Topic(topic), message, Ttl(ttl), tag, prompt) - val publishRequest = RelayDTO.Publish.Request(id = id ?: generateClientToServerId(), params = publishParams) - - observePublishResult(publishRequest.id, onResult) - relayService.publishRequest(publishRequest) - } - - private fun observePublishResult(id: Long, onResult: (Result) -> Unit) { - scope.launch { - try { - withTimeout(RESULT_TIMEOUT) { - resultState - .filterIsInstance() - .filter { relayResult -> relayResult.id == id } - .first { publishResult -> - when (publishResult) { - is RelayDTO.Publish.Result.Acknowledgement -> onResult(Result.success(publishResult.toRelay())) - is RelayDTO.Publish.Result.JsonRpcError -> onResult(Result.failure(Throwable(publishResult.error.errorMessage))) - } - true - } - } - } catch (e: TimeoutCancellationException) { - onResult(Result.failure(e)) - cancelJobIfActive() - } catch (e: Exception) { - onResult(Result.failure(e)) - cancelJobIfActive() - } - } - } - - @ExperimentalCoroutinesApi - override fun subscribe(topic: String, id: Long?, onResult: (Result) -> Unit) { - val subscribeRequest = RelayDTO.Subscribe.Request(id = id ?: generateClientToServerId(), params = RelayDTO.Subscribe.Request.Params(Topic(topic))) - - if (isLoggingEnabled) { - logger.log("Sending SubscribeRequest: $subscribeRequest; timestamp: ${System.currentTimeMillis()}") - } - - observeSubscribeResult(subscribeRequest.id, onResult) - relayService.subscribeRequest(subscribeRequest) - } - - private fun observeSubscribeResult(id: Long, onResult: (Result) -> Unit) { - scope.launch { - try { - withTimeout(RESULT_TIMEOUT) { - if (isLoggingEnabled) println("ObserveSubscribeResult: $id; timestamp: ${System.currentTimeMillis()}") - resultState - .onEach { relayResult -> if (isLoggingEnabled) logger.log("SubscribeResult 1: $relayResult") } - .filterIsInstance() - .onEach { relayResult -> if (isLoggingEnabled) logger.log("SubscribeResult 2: $relayResult") } - .filter { relayResult -> relayResult.id == id } - .first { subscribeResult -> - if (isLoggingEnabled) println("SubscribeResult 3: $subscribeResult") - when (subscribeResult) { - is RelayDTO.Subscribe.Result.Acknowledgement -> onResult(Result.success(subscribeResult.toRelay())) - is RelayDTO.Subscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(subscribeResult.error.errorMessage))) - } - true - } - } - } catch (e: TimeoutCancellationException) { - onResult(Result.failure(e)) - cancelJobIfActive() - } catch (e: Exception) { - onResult(Result.failure(e)) - cancelJobIfActive() - } - } - } - - @ExperimentalCoroutinesApi - override fun batchSubscribe(topics: List, id: Long?, onResult: (Result) -> Unit) { - val batchSubscribeRequest = RelayDTO.BatchSubscribe.Request(id = id ?: generateClientToServerId(), params = RelayDTO.BatchSubscribe.Request.Params(topics)) - - observeBatchSubscribeResult(batchSubscribeRequest.id, onResult) - relayService.batchSubscribeRequest(batchSubscribeRequest) - } - - private fun observeBatchSubscribeResult(id: Long, onResult: (Result) -> Unit) { - scope.launch { - try { - withTimeout(RESULT_TIMEOUT) { - resultState - .filterIsInstance() - .filter { relayResult -> relayResult.id == id } - .first { batchSubscribeResult -> - when (batchSubscribeResult) { - is RelayDTO.BatchSubscribe.Result.Acknowledgement -> onResult(Result.success(batchSubscribeResult.toRelay())) - is RelayDTO.BatchSubscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(batchSubscribeResult.error.errorMessage))) - } - true - } - } - } catch (e: TimeoutCancellationException) { - onResult(Result.failure(e)) - cancelJobIfActive() - } catch (e: Exception) { - onResult(Result.failure(e)) - cancelJobIfActive() - } - } - } - - @ExperimentalCoroutinesApi - override fun unsubscribe( - topic: String, - subscriptionId: String, - id: Long?, - onResult: (Result) -> Unit, - ) { - val unsubscribeRequest = RelayDTO.Unsubscribe.Request( - id = id ?: generateClientToServerId(), - params = RelayDTO.Unsubscribe.Request.Params(Topic(topic), SubscriptionId(subscriptionId)) - ) - - observeUnsubscribeResult(unsubscribeRequest.id, onResult) - relayService.unsubscribeRequest(unsubscribeRequest) - } - - private fun observeUnsubscribeResult(id: Long, onResult: (Result) -> Unit) { - scope.launch { - try { - withTimeout(RESULT_TIMEOUT) { - resultState - .filterIsInstance() - .filter { relayResult -> relayResult.id == id } - .first { unsubscribeResult -> - when (unsubscribeResult) { - is RelayDTO.Unsubscribe.Result.Acknowledgement -> onResult(Result.success(unsubscribeResult.toRelay())) - is RelayDTO.Unsubscribe.Result.JsonRpcError -> onResult(Result.failure(Throwable(unsubscribeResult.error.errorMessage))) - } - true - } - } - } catch (e: TimeoutCancellationException) { - onResult(Result.failure(e)) - cancelJobIfActive() - } catch (e: Exception) { - onResult(Result.failure(e)) - cancelJobIfActive() - } - } - } - - private fun publishSubscriptionAcknowledgement(id: Long) { - val publishRequest = RelayDTO.Subscription.Result.Acknowledgement(id = id, result = true) - relayService.publishSubscriptionAcknowledgement(publishRequest) - } - - private fun CoroutineScope.cancelJobIfActive() { - if (this.coroutineContext.job.isActive) { - this.coroutineContext.job.cancel() - } - } - - private companion object { - const val REPLAY: Int = 1 - const val RESULT_TIMEOUT: Long = 60000 - } -} \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/Scope.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/util/Scope.kt deleted file mode 100644 index 248e8a79b..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/Scope.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.foundation.util - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob - -internal val job = SupervisorJob() -internal var scope = CoroutineScope(job + Dispatchers.IO) \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/UtilFunctions.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/util/UtilFunctions.kt deleted file mode 100644 index 54413a71b..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/UtilFunctions.kt +++ /dev/null @@ -1,62 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") - -package com.walletconnect.util - -import java.security.SecureRandom -import kotlin.math.pow - -@get:JvmSynthetic -val String.Companion.Empty - get() = "" - -fun generateId(): Long = ("${System.currentTimeMillis()}${(100..999).random()}").toLong() - -fun generateClientToServerId(): Long { - val now = System.currentTimeMillis() * 10.0.pow(6.0) - return now.plus((100000..999999).random()).toLong() -} - -fun randomBytes(size: Int): ByteArray = ByteArray(size).apply { - SecureRandom().nextBytes(this) -} - -fun ByteArray.bytesToHex(): String { - val hexString = StringBuilder(2 * this.size) - - this.indices.forEach { i -> - val hex = Integer.toHexString(0xff and this[i].toInt()) - - if (hex.length == 1) { - hexString.append('0') - } - - hexString.append(hex) - } - - return hexString.toString() -} - -fun ByteArray.bytesToInt(size: Int): Int { - require(this.size <= 4) { "Byte array size must be less than 5" } - - return (0 until size).fold(0) { acc, i -> - acc.or(this[i].toInt().shl((size - 1 - i) * 8)) - } -} - -fun String.hexToBytes(): ByteArray { - val len = this.length - val data = ByteArray(len / 2) - var i = 0 - - while (i < len) { - data[i / 2] = ((Character.digit(this[i], 16) shl 4) - + Character.digit(this[i + 1], 16)).toByte() - i += 2 - } - - return data -} - -@JvmSynthetic -internal fun String.addUserAgent(sdkVersion: String): String = "$this&ua=wc-2/kotlin-$sdkVersion" \ No newline at end of file diff --git a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtClaims.kt b/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtClaims.kt deleted file mode 100644 index 69bbd4eb4..000000000 --- a/foundation/src/main/kotlin/com/walletconnect/foundation/util/jwt/JwtClaims.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.foundation.util.jwt - -interface JwtClaims { - val issuer: String -} \ No newline at end of file diff --git a/foundation/src/test/kotlin/com/reown/foundation/BaseRelayClientTest.kt b/foundation/src/test/kotlin/com/reown/foundation/BaseRelayClientTest.kt new file mode 100644 index 000000000..499362f96 --- /dev/null +++ b/foundation/src/test/kotlin/com/reown/foundation/BaseRelayClientTest.kt @@ -0,0 +1,288 @@ +package com.reown.foundation + +import com.tinder.scarlet.WebSocket +import com.reown.foundation.common.model.SubscriptionId +import com.reown.foundation.network.BaseRelayClient +import com.reown.foundation.network.ConnectionLifecycle +import com.reown.foundation.network.ConnectionState +import com.reown.foundation.network.data.service.RelayService +import com.reown.foundation.network.model.Relay +import com.reown.foundation.network.model.RelayDTO +import com.reown.foundation.util.Logger +import com.reown.foundation.util.scope +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.slot +import io.mockk.spyk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import kotlinx.coroutines.withContext +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +@ExperimentalCoroutinesApi +class BaseRelayClientTest { + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + private lateinit var client: BaseRelayClient + private val relayServiceMock = mockk(relaxed = true) + private val connectionLifecycleMock = mockk(relaxed = true) + private val loggerMock = mockk(relaxed = true) + private val mockConnectionState = MutableStateFlow(ConnectionState.Idle) + + @Before + fun setup() { + Dispatchers.setMain(testDispatcher) + + client = spyk(object : BaseRelayClient() { + init { + this.relayService = relayServiceMock + this.logger = loggerMock + scope = testScope + this.connectionLifecycle = connectionLifecycleMock + } + }, recordPrivateCalls = true) + client.connectionState = mockConnectionState + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun `publish invokes relayService publishRequest successfully`() = testScope.runTest { + val id = 123L + + val topic = "testTopic" + val message = "testMessage" + val params = Relay.Model.IrnParams(1, 60, true) + val ack = RelayDTO.Publish.Result.Acknowledgement(123L, result = true) + + val publishRequestSlot = slot() + coEvery { relayServiceMock.publishRequest(capture(publishRequestSlot)) } just Runs + coEvery { relayServiceMock.observePublishAcknowledgement() } returns flowOf(ack) + every { connectionLifecycleMock.reconnect() } just Runs + coEvery { relayServiceMock.observeWebSocketEvent() } returns flowOf(WebSocket.Event.OnConnectionOpened("Open")) + + client.connectionState.value = ConnectionState.Open + client.publish(topic, message, params, id) + + coVerify { relayServiceMock.publishRequest(any()) } + assertEquals(id, publishRequestSlot.captured.id) + } + + @Test + fun `test publish success`() = testScope.runTest { + val topic = "testTopic" + val message = "testMessage" + val params = Relay.Model.IrnParams(1, 60, true) + val ack = RelayDTO.Publish.Result.Acknowledgement(123L, result = true) + + coEvery { relayServiceMock.publishRequest(any()) } returns Unit + coEvery { relayServiceMock.observePublishAcknowledgement() } returns flowOf(ack) + coEvery { relayServiceMock.observeWebSocketEvent() } returns flowOf(WebSocket.Event.OnConnectionOpened("Open")) + + client.observeResults() + client.connectionState.value = ConnectionState.Open + client.publish(topic, message, params, 123L) { result -> + result.fold( + onSuccess = { + assertEquals(123L, it.id) + }, + onFailure = { fail(it.message) } + ) + } + + coVerify { relayServiceMock.publishRequest(any()) } + } + + + @Test + fun `test publish error due to time out`() = testScope.runTest() { + val topic = "testTopic" + val message = "testMessage" + val params = Relay.Model.IrnParams(1, 60, true) + + coEvery { relayServiceMock.publishRequest(any()) } returns Unit + coEvery { relayServiceMock.observePublishAcknowledgement() } returns flow { delay(15000L) } + + withContext(Dispatchers.Default.limitedParallelism(1)) { + client.connectionState.value = ConnectionState.Open + client.publish(topic, message, params) { result -> + result.fold( + onSuccess = { + fail("Should not be successful") + }, + onFailure = { + assertTrue(result.exceptionOrNull() is TimeoutCancellationException) + } + ) + } + } + + advanceUntilIdle() + + coVerify { relayServiceMock.publishRequest(any()) } + } + + @Test + fun `test subscribe success`() = testScope.runTest { + val topic = "testTopic" + val expectedId = 123L + val relayDto = RelayDTO.Subscribe.Result.Acknowledgement(id = expectedId, result = SubscriptionId("testId")) + + coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flowOf(relayDto) + + client.observeResults() + client.connectionState.value = ConnectionState.Open + client.subscribe(topic, expectedId) { result -> + result.fold( + onSuccess = { + assertEquals(expectedId, result.getOrNull()?.id) + }, + onFailure = { fail(it.message) } + ) + } + + coVerify { relayServiceMock.subscribeRequest(any()) } + } + + @Test + fun `test subscribe failure due to timeout`() = testScope.runTest() { + val topic = "testTopic" + + coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flow { delay(10000L) } + + client.connectionState.value = ConnectionState.Open + client.subscribe(topic) { result -> + result.fold( + onSuccess = { + fail("Should not be successful") + }, + onFailure = { + assertTrue(result.exceptionOrNull() is TimeoutCancellationException) + } + ) + } + + testScheduler.apply { advanceTimeBy(5000); runCurrent() } + + coVerify { relayServiceMock.subscribeRequest(any()) } + } + + @Test + fun `test batch subscribe success`() = testScope.runTest { + val topics = listOf("testTopic") + val expectedId = 123L + val relayDto = RelayDTO.BatchSubscribe.Result.Acknowledgement(id = expectedId, result = listOf("testId")) + + coEvery { relayServiceMock.batchSubscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeBatchSubscribeAcknowledgement() } returns flowOf(relayDto) + + client.observeResults() + client.connectionState.value = ConnectionState.Open + client.batchSubscribe(topics, expectedId) { result -> + result.fold( + onSuccess = { + assertEquals(expectedId, result.getOrNull()?.id) + }, + onFailure = { fail(it.message) } + ) + } + + coVerify { relayServiceMock.batchSubscribeRequest(any()) } + } + + @Test + fun `test batch subscribe failure due to timeout`() = testScope.runTest { + val topics = listOf("testTopic") + + coEvery { relayServiceMock.batchSubscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeBatchSubscribeAcknowledgement() } returns flow { delay(10000L) } + + client.batchSubscribe(topics) { result -> + result.fold( + onSuccess = { + fail("Should not be successful") + }, + onFailure = { + assertTrue(result.exceptionOrNull() is TimeoutCancellationException) + } + ) + + } + + testScheduler.apply { advanceTimeBy(5000); runCurrent() } + + coVerify { relayServiceMock.batchSubscribeRequest(any()) } + } + + @Test + fun `test unsubscribe success`() = testScope.runTest { + val topic = "testTopic" + val expectedId = 123L + val relayDto = RelayDTO.Unsubscribe.Result.Acknowledgement(id = expectedId, result = true) + + coEvery { relayServiceMock.unsubscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeUnsubscribeAcknowledgement() } returns flowOf(relayDto) + + client.observeResults() + client.connectionState.value = ConnectionState.Open + client.unsubscribe(topic, "subsId", expectedId) { result -> + result.fold( + onSuccess = { + assertEquals(expectedId, result.getOrNull()?.id) + }, + onFailure = { fail(it.message) } + ) + } + + coVerify { relayServiceMock.unsubscribeRequest(any()) } + } + + @Test + fun `test unsubscribe failure`() = testScope.runTest { + val topic = "testTopic" + + coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit + coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flow { delay(10000L) } + + client.connectionState.value = ConnectionState.Open + client.subscribe(topic) { result -> + result.fold( + onSuccess = { + fail("Should not be successful") + }, + onFailure = { + assertTrue(result.exceptionOrNull() is TimeoutCancellationException) + } + ) + } + + testScheduler.apply { advanceTimeBy(5000); runCurrent() } + + coVerify { relayServiceMock.subscribeRequest(any()) } + } +} \ No newline at end of file diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/RelayTest.kt b/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt similarity index 93% rename from foundation/src/test/kotlin/com/walletconnect/foundation/RelayTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt index 91e03bc0f..505e164b4 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/RelayTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt @@ -1,15 +1,14 @@ -package com.walletconnect.foundation - -import com.walletconnect.foundation.crypto.data.repository.ClientIdJwtRepository -import com.walletconnect.foundation.di.FoundationDITags -import com.walletconnect.foundation.di.cryptoModule -import com.walletconnect.foundation.di.foundationCommonModule -import com.walletconnect.foundation.di.networkModule -import com.walletconnect.foundation.network.BaseRelayClient -import com.walletconnect.foundation.network.RelayInterface -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.util.addUserAgent -import com.walletconnect.util.bytesToHex +package com.reown.foundation + +import com.reown.foundation.crypto.data.repository.ClientIdJwtRepository +import com.reown.foundation.di.FoundationDITags +import com.reown.foundation.di.cryptoModule +import com.reown.foundation.di.foundationCommonModule +import com.reown.foundation.di.networkModule +import com.reown.foundation.network.BaseRelayClient +import com.reown.foundation.network.RelayInterface +import com.reown.foundation.network.model.Relay +import com.reown.util.* import junit.framework.TestCase.assertEquals import junit.framework.TestCase.fail import kotlinx.coroutines.CompletableJob diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/UtilsTest.kt b/foundation/src/test/kotlin/com/reown/foundation/UtilsTest.kt similarity index 78% rename from foundation/src/test/kotlin/com/walletconnect/foundation/UtilsTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/UtilsTest.kt index 70f572461..3f2cd369d 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/UtilsTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/UtilsTest.kt @@ -1,7 +1,6 @@ -package com.walletconnect.foundation +package com.reown.foundation -import com.walletconnect.util.generateClientToServerId -import com.walletconnect.util.generateId +import com.reown.util.* import junit.framework.TestCase.assertTrue import org.junit.Test diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/SubscriptionIdAdapterTest.kt b/foundation/src/test/kotlin/com/reown/foundation/adapters/SubscriptionIdAdapterTest.kt similarity index 81% rename from foundation/src/test/kotlin/com/walletconnect/foundation/adapters/SubscriptionIdAdapterTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/adapters/SubscriptionIdAdapterTest.kt index d2300c273..eb473fd96 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/SubscriptionIdAdapterTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/adapters/SubscriptionIdAdapterTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.foundation.adapters +package com.reown.foundation.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.walletconnect.foundation.common.adapters.SubscriptionIdAdapter -import com.walletconnect.foundation.common.model.SubscriptionId +import com.reown.foundation.common.adapters.SubscriptionIdAdapter +import com.reown.foundation.common.model.SubscriptionId import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull import org.junit.Test diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TopicAdapterTest.kt b/foundation/src/test/kotlin/com/reown/foundation/adapters/TopicAdapterTest.kt similarity index 85% rename from foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TopicAdapterTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/adapters/TopicAdapterTest.kt index a04885ec7..1e81da86f 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TopicAdapterTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/adapters/TopicAdapterTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.foundation.adapters +package com.reown.foundation.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.walletconnect.foundation.common.adapters.TopicAdapter -import com.walletconnect.foundation.common.model.Topic +import com.reown.foundation.common.adapters.TopicAdapter +import com.reown.foundation.common.model.Topic import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull import org.junit.Test diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TtlAdapterTest.kt b/foundation/src/test/kotlin/com/reown/foundation/adapters/TtlAdapterTest.kt similarity index 77% rename from foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TtlAdapterTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/adapters/TtlAdapterTest.kt index 539a2bdc4..6c56f53c2 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/adapters/TtlAdapterTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/adapters/TtlAdapterTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.foundation.adapters +package com.reown.foundation.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.walletconnect.foundation.common.adapters.TtlAdapter -import com.walletconnect.foundation.common.model.Ttl +import com.reown.foundation.common.adapters.TtlAdapter +import com.reown.foundation.common.model.Ttl import junit.framework.TestCase.assertEquals import org.junit.Test diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt b/foundation/src/test/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt similarity index 86% rename from foundation/src/test/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt rename to foundation/src/test/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt index 8db23b58f..812ab378f 100644 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/crypto/data/repository/ClientIdJwtRepositoryTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.foundation.crypto.data.repository +package com.reown.foundation.crypto.data.repository -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.jwtIatAndExp +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.jwtIatAndExp import io.mockk.every import io.mockk.mockkStatic import io.mockk.spyk diff --git a/foundation/src/test/kotlin/com/walletconnect/foundation/BaseRelayClientTest.kt b/foundation/src/test/kotlin/com/walletconnect/foundation/BaseRelayClientTest.kt deleted file mode 100644 index 9bc21c2f1..000000000 --- a/foundation/src/test/kotlin/com/walletconnect/foundation/BaseRelayClientTest.kt +++ /dev/null @@ -1,245 +0,0 @@ -package com.walletconnect.foundation - -import com.walletconnect.foundation.common.model.SubscriptionId -import com.walletconnect.foundation.network.BaseRelayClient -import com.walletconnect.foundation.network.data.service.RelayService -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.foundation.network.model.RelayDTO -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.scope -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.mockk -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain -import kotlinx.coroutines.withContext -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Test - -@ExperimentalCoroutinesApi -class BaseRelayClientTest { - private val testDispatcher = StandardTestDispatcher() - private val testScope = TestScope(testDispatcher) - private lateinit var client: BaseRelayClient - private val relayServiceMock = mockk(relaxed = true) - private val loggerMock = mockk(relaxed = true) - - @Before - fun setup() { - Dispatchers.setMain(testDispatcher) - - client = object : BaseRelayClient() { - init { - this.relayService = relayServiceMock - this.logger = loggerMock - scope = testScope - } - } - } - - @After - fun tearDown() { - Dispatchers.resetMain() - } - - @Test - fun `test publish success`() = testScope.runTest { - val topic = "testTopic" - val message = "testMessage" - val params = Relay.Model.IrnParams(1, 60, true) - val ack = RelayDTO.Publish.Result.Acknowledgement(123L, result = true) - - coEvery { relayServiceMock.publishRequest(any()) } returns Unit - coEvery { relayServiceMock.observePublishAcknowledgement() } returns flowOf(ack) - - client.observeResults() - client.publish(topic, message, params, 123L) { result -> - result.fold( - onSuccess = { - assertEquals(123L, it.id) - }, - onFailure = { fail(it.message) } - ) - } - - coVerify { relayServiceMock.publishRequest(any()) } - } - - @Test - fun `test publish error due to time out`() = testScope.runTest { - val topic = "testTopic" - val message = "testMessage" - val params = Relay.Model.IrnParams(1, 60, true) - - coEvery { relayServiceMock.publishRequest(any()) } returns Unit - coEvery { relayServiceMock.observePublishAcknowledgement() } returns flow { delay(10000L) } - - withContext(Dispatchers.Default.limitedParallelism(1)) { - client.publish(topic, message, params) { result -> - result.fold( - onSuccess = { - fail("Should not be successful") - }, - onFailure = { - assertTrue(result.exceptionOrNull() is TimeoutCancellationException) - } - ) - } - } - - advanceUntilIdle() - - coVerify { relayServiceMock.publishRequest(any()) } - } - - @Test - fun `test subscribe success`() = testScope.runTest { - val topic = "testTopic" - val expectedId = 123L - val relayDto = RelayDTO.Subscribe.Result.Acknowledgement(id = expectedId, result = SubscriptionId("testId")) - - coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flowOf(relayDto) - - client.observeResults() - client.subscribe(topic, expectedId) { result -> - result.fold( - onSuccess = { - assertEquals(expectedId, result.getOrNull()?.id) - }, - onFailure = { fail(it.message) } - ) - } - - coVerify { relayServiceMock.subscribeRequest(any()) } - } - - @Test - fun `test subscribe failure due to timeout`() = testScope.runTest() { - val topic = "testTopic" - - coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flow { delay(10000L) } - - client.subscribe(topic) { result -> - result.fold( - onSuccess = { - fail("Should not be successful") - }, - onFailure = { - assertTrue(result.exceptionOrNull() is TimeoutCancellationException) - } - ) - } - - testScheduler.apply { advanceTimeBy(5000); runCurrent() } - - coVerify { relayServiceMock.subscribeRequest(any()) } - } - - @Test - fun `test batch subscribe success`() = testScope.runTest { - val topics = listOf("testTopic") - val expectedId = 123L - val relayDto = RelayDTO.BatchSubscribe.Result.Acknowledgement(id = expectedId, result = listOf("testId")) - - coEvery { relayServiceMock.batchSubscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeBatchSubscribeAcknowledgement() } returns flowOf(relayDto) - - client.observeResults() - client.batchSubscribe(topics, expectedId) { result -> - result.fold( - onSuccess = { - assertEquals(expectedId, result.getOrNull()?.id) - }, - onFailure = { fail(it.message) } - ) - } - - coVerify { relayServiceMock.batchSubscribeRequest(any()) } - } - - @Test - fun `test batch subscribe failure due to timeout`() = testScope.runTest { - val topics = listOf("testTopic") - - coEvery { relayServiceMock.batchSubscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeBatchSubscribeAcknowledgement() } returns flow { delay(10000L) } - - client.batchSubscribe(topics) { result -> - result.fold( - onSuccess = { - fail("Should not be successful") - }, - onFailure = { - assertTrue(result.exceptionOrNull() is TimeoutCancellationException) - } - ) - - } - - testScheduler.apply { advanceTimeBy(5000); runCurrent() } - - coVerify { relayServiceMock.batchSubscribeRequest(any()) } - } - - @Test - fun `test unsubscribe success`() = testScope.runTest { - val topic = "testTopic" - val expectedId = 123L - val relayDto = RelayDTO.Unsubscribe.Result.Acknowledgement(id = expectedId, result = true) - - coEvery { relayServiceMock.unsubscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeUnsubscribeAcknowledgement() } returns flowOf(relayDto) - - client.observeResults() - client.unsubscribe(topic, "subsId", expectedId) { result -> - result.fold( - onSuccess = { - assertEquals(expectedId, result.getOrNull()?.id) - }, - onFailure = { fail(it.message) } - ) - } - - coVerify { relayServiceMock.unsubscribeRequest(any()) } - } - - @Test - fun `test unsubscribe failure`() = testScope.runTest { - val topic = "testTopic" - - coEvery { relayServiceMock.subscribeRequest(any()) } returns Unit - coEvery { relayServiceMock.observeSubscribeAcknowledgement() } returns flow { delay(10000L) } - - client.subscribe(topic) { result -> - result.fold( - onSuccess = { - fail("Should not be successful") - }, - onFailure = { - assertTrue(result.exceptionOrNull() is TimeoutCancellationException) - } - ) - } - - testScheduler.apply { advanceTimeBy(5000); runCurrent() } - - coVerify { relayServiceMock.subscribeRequest(any()) } - } - -} \ No newline at end of file diff --git a/gradle/consumer-rules/walletconnect-rules.pro b/gradle/consumer-rules/walletconnect-rules.pro index 7e37a83b3..e622a83c5 100644 --- a/gradle/consumer-rules/walletconnect-rules.pro +++ b/gradle/consumer-rules/walletconnect-rules.pro @@ -1,5 +1,6 @@ --keep class com.walletconnect.android.** { *; } +-keep class com.reown.android.** { *; } -keep interface com.walletconnect.** { *; } +-keep interface com.reown.** { *; } -keep class kotlinx.coroutines.** { *; } -dontwarn kotlinx.coroutines.** diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9b92764b1..ead72ccb4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,8 +20,8 @@ coroutines = "1.8.1" sqlDelight = "2.0.2" dokka = "1.9.20" moshi = "1.15.1" -googleService = "4.4.1" -scarlet = "1.0.1" +googleService = "4.4.2" +scarlet = "1.0.2" koin = "3.5.6" retrofit = "2.11.0" okhttp = "4.12.0" @@ -38,18 +38,18 @@ customQrGenerator = "1.6.2" beagle = "2.9.0" coinbase = "1.0.4" -firebaseBOM = "33.0.0" +firebaseBOM = "33.3.0" firebaseAppDistribution = "5.0.0" androidxCore = "1.13.1" -androidxAppCompat = "1.6.1" +androidxAppCompat = "1.7.0" andoridxMaterial = "1.12.0" -androidxLifecycle = "2.8.0" -androidxTest = "1.5.0" -androidxTestOrchestration = "1.4.2" +androidxLifecycle = "2.8.5" +androidxTest = "1.6.1" +androidxTestOrchestration = "1.5.0" androidxSecurity = "1.1.0-alpha06" androidxDatastore = "1.1.1" -androidxNavigation = "2.7.7" +androidxNavigation = "2.8.0" jUnit = "4.13.2" robolectric = "4.12.2" @@ -78,10 +78,10 @@ androidx-datastore = { module = "androidx.datastore:datastore-preferences", vers androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } androidx-appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppCompat" } androidx-material = { module = "com.google.android.material:material", version.ref = "andoridxMaterial" } -androidx-testJunit = { module = "androidx.test.ext:junit-ktx", version = "1.1.5" } +androidx-testJunit = { module = "androidx.test.ext:junit-ktx", version = "1.2.1" } androidx-testCore = { module = "androidx.test:core-ktx", version.ref = "androidxTest" } androidx-testOrchestrator = { module = "androidx.test:orchestrator", version.ref = "androidxTestOrchestration" } -androidx-testRunner = { module = "androidx.test:runner", version = "1.5.2" } +androidx-testRunner = { module = "androidx.test:runner", version = "1.6.2" } androidx-testRules = { module = "androidx.test:rules", version.ref = "androidxTest" } #-------------------------- Compose -------------------------- androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBOM" } @@ -89,11 +89,11 @@ androidx-compose-ui = { module = "androidx.compose.ui:ui" } androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } -androidx-compose-ui-test-junit = { module = "androidx.compose.ui:ui-test-junit4", version = "1.6.7" } +androidx-compose-ui-test-junit = { module = "androidx.compose.ui:ui-test-junit4", version = "1.7.1" } androidx-compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" } androidx-compose-navigation-testing = { module = "androidx.navigation:navigation-testing", version.ref = "androidxNavigation" } androidx-compose-lifecycle = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "composeViewModel" } -androidx-compose-material = { module = "androidx.compose.material:material", version = "1.6.7" } +androidx-compose-material = { module = "androidx.compose.material:material", version = "1.7.1" } coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } @@ -122,7 +122,7 @@ koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" } koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin" } jUnit = { module = "junit:junit", version.ref = "jUnit" } -jUnit-engine = { module = "org.junit.vintage:junit-vintage-engine", version = "5.10.0" } +jUnit-engine = { module = "org.junit.vintage:junit-vintage-engine", version = "5.11.0" } kethereum-bip39 = { module = "com.github.komputing.kethereum:bip39", version.ref = "kethereum" } kethereum-bip39-wordlist = { module = "com.github.komputing.kethereum:bip39_wordlist_en", version.ref = "kethereum" } @@ -195,7 +195,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -firebase-crashlytics = { id = "com.google.firebase.crashlytics", version = "3.0.1" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version = "3.0.2" } firebase-distribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } google-services = { id = "com.google.gms.google-services", version.ref = "googleService" } diff --git a/product/web3modal/.gitignore b/product/appkit/.gitignore similarity index 100% rename from product/web3modal/.gitignore rename to product/appkit/.gitignore diff --git a/product/appkit/ReadMe.md b/product/appkit/ReadMe.md new file mode 100644 index 000000000..c5dc98fdc --- /dev/null +++ b/product/appkit/ReadMe.md @@ -0,0 +1,34 @@ +# **AppKit - Kotlin** + +# Installation + +Kotlin implementation of AppKit for Android applications. + +Android Core ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/android-core) + +AppKit ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/web3modal) + +## Requirements + +* Android min SDK 23 +* Java 11 + +## Installation +root/build.gradle.kts: +```gradle +allprojects { + repositories { + mavenCentral() + maven { url "https://jitpack.io" } + } +} +``` + +app/build.gradle.kts + +```gradle +implementation(platform("com.reown:android-bom:$BOM_VERSION")) +implementation("com.reown:android-core") +implementation("com.reown:appkit") +``` + diff --git a/product/appkit/build.gradle.kts b/product/appkit/build.gradle.kts new file mode 100644 index 000000000..762de8cc7 --- /dev/null +++ b/product/appkit/build.gradle.kts @@ -0,0 +1,107 @@ +plugins { + id("com.android.library") + id(libs.plugins.kotlin.android.get().pluginId) + alias(libs.plugins.google.ksp) + alias(libs.plugins.paparazzi) + id("publish-module-android") + id("jacoco-report") +} + +project.apply { + extra[KEY_PUBLISH_ARTIFACT_ID] = APPKIT + extra[KEY_PUBLISH_VERSION] = APPKIT_VERSION + extra[KEY_SDK_NAME] = "appkit" +} + +android { + namespace = "com.reown.appkit" + compileSdk = COMPILE_SDK + + defaultConfig { + minSdk = MIN_SDK + + aarMetadata { + minCompileSdk = MIN_SDK + } + + buildConfigField(type = "String", name = "SDK_VERSION", value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"") + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + File("${rootDir.path}/gradle/consumer-rules").listFiles()?.let { proguardFiles -> + consumerProguardFiles(*proguardFiles) + } + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro") + } + } + lint { + abortOnError = true + ignoreWarnings = true + warningsAsErrors = false + } + + compileOptions { + sourceCompatibility = jvmVersion + targetCompatibility = jvmVersion + } + kotlinOptions { + jvmTarget = jvmVersion.toString() + freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.time.ExperimentalTime" + } + buildFeatures { + compose = true + buildConfig = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + + tasks.withType(Test::class.java) { + jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods") + } +} + +dependencies { + + implementation(libs.bundles.androidxAppCompat) + implementation(libs.bundles.accompanist) + implementation(libs.coil) + + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.compose.material) + implementation(libs.androidx.compose.navigation) + implementation(libs.androidx.compose.lifecycle) + debugImplementation(libs.androidx.compose.ui.tooling) + debugImplementation(libs.androidx.compose.ui.test.manifest) + androidTestImplementation(libs.androidx.compose.ui.test.junit) + androidTestImplementation(libs.androidx.compose.navigation.testing) + + implementation(libs.androidx.datastore) + implementation(libs.bundles.androidxLifecycle) + ksp(libs.moshi.ksp) + api(libs.bundles.androidxNavigation) + implementation(libs.qrCodeGenerator) + implementation(libs.coinbaseWallet) + + testImplementation(libs.coroutines.test) + testImplementation(libs.turbine) + + releaseImplementation("com.reown:android-core:$CORE_VERSION") + releaseImplementation("com.reown:sign:$SIGN_VERSION") + releaseImplementation("com.reown:modal-core:$MODAL_CORE_VERSION") + + debugImplementation(project(":core:android")) + debugImplementation(project(":protocol:sign")) + debugImplementation(project(":core:modal")) + + testImplementation(libs.bundles.androidxTest) + + androidTestUtil(libs.androidx.testOrchestrator) + androidTestImplementation(libs.bundles.androidxAndroidTest) +} \ No newline at end of file diff --git a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/TestUtils.kt b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/TestUtils.kt similarity index 86% rename from product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/TestUtils.kt rename to product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/TestUtils.kt index 1cb3be060..28ebcbc60 100644 --- a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/TestUtils.kt +++ b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/TestUtils.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui +package com.reown.appkit.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -8,8 +8,8 @@ import androidx.navigation.compose.ComposeNavigator import androidx.navigation.compose.composable import androidx.navigation.createGraph import androidx.navigation.testing.TestNavHostController -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.addTitleArg +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.addTitleArg @Composable fun rememberTestNavController(): TestNavHostController { diff --git a/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBarTest.kt b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBarTest.kt new file mode 100644 index 000000000..8388e523d --- /dev/null +++ b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBarTest.kt @@ -0,0 +1,47 @@ +package com.reown.appkit.ui.components.internal + +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import com.reown.appkit.ui.components.internal.commons.BackArrowIcon +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.components.internal.commons.TestTags +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import org.junit.Rule +import org.junit.Test +import org.junit.Assert.* + +class AppKitTopBarTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun web3ModalTopBar_titleShouldBeShown() { + composeTestRule.setContent { + ProvideAppKitThemeComposition { + AppKitTopBar(title = "Title", startIcon = { BackArrowIcon { } }, onCloseIconClick = {}) + } + } + + composeTestRule.onNodeWithTag(TestTags.TITLE).assertExists() + composeTestRule.onNodeWithText("Title").assertExists() + } + + @Test + fun web3ModalTopBar_onCloseCallbackIsTriggered() { + var isClicked = false + + composeTestRule.setContent { + ProvideAppKitThemeComposition { + AppKitTopBar(title = "Title", startIcon = { BackArrowIcon { } }, onCloseIconClick = { isClicked = true}) + } + + } + composeTestRule.onNodeWithContentDescription(ContentDescription.CLOSE.description).performClick() + + assertEquals(isClicked, true) + } +} \ No newline at end of file diff --git a/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootStateTest.kt b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootStateTest.kt new file mode 100644 index 000000000..65516141b --- /dev/null +++ b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootStateTest.kt @@ -0,0 +1,136 @@ +package com.reown.appkit.ui.components.internal.root + +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.test.junit4.createComposeRule +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.rememberTestNavController +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test + +class AppKitRootStateTest { + + @get:Rule + val composeTestRule = createComposeRule() + + private lateinit var state: AppKitRootState + + @Test + fun web3modalRootState_currentDestination() = runTest { + var currentDestinationPath: String? = null + + composeTestRule.setContent { + val navController = rememberTestNavController() + state = remember(navController) { + AppKitRootState( + coroutineScope = backgroundScope, + navController = navController + ) + } + + currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route + + LaunchedEffect(Unit) { + navController.setCurrentDestination(Route.QR_CODE.path) + } + } + + assertEquals(Route.QR_CODE.path, currentDestinationPath) + } + + @Test + fun web3ModalRootState_canPopup_fromInitDestination() = runTest { + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + } + + assertEquals(false, state.canPopUp) + } + + @Test + fun web3ModalRootState_canPopup_fromQRCode() = runTest { + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + + LaunchedEffect(Unit) { + navController.setCurrentDestination(Route.QR_CODE.path) + } + } + + assertEquals(true, state.canPopUp) + } + + @Test + fun web3ModalRouteState_popUp_shouldNavigateBack() = runTest { + var currentDestinationPath: String? = null + + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + + currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route + + LaunchedEffect(Unit) { + navController.navigate(Route.QR_CODE.path) + state.popUp() + } + } + + assertEquals(Route.CONNECT_YOUR_WALLET.path, currentDestinationPath) + } + + @Test + fun web3ModalRouteState_title_shouldBeTakenFromEnum() = runTest { + var title: String? = null + + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + + title = state.title.collectAsState(initial = null).value + } + + assertEquals(Route.CONNECT_YOUR_WALLET.title, title) + } + + @Test + fun web3ModalRouteState_title_shouldBeTakenFromArg() = runTest { + var title: String? = null + + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + + title = state.title.collectAsState(initial = null).value + + LaunchedEffect(Unit) { + navController.navigate(Route.REDIRECT.path + "&" + "title") + } + } + + assertEquals("title", title) + } + + @Test + fun web3ModalRouteState_navigateToHelp() = runTest { + var currentDestinationPath: String? = null + + composeTestRule.setContent { + val navController = rememberTestNavController() + state = rememberAppKitRootState(coroutineScope = backgroundScope, navController = navController) + + currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route + + LaunchedEffect(Unit) { + state.navigateToHelp() + } + } + + assertEquals(Route.WHAT_IS_WALLET.path, currentDestinationPath) + } +} diff --git a/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootTest.kt b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootTest.kt new file mode 100644 index 000000000..f71b472ec --- /dev/null +++ b/product/appkit/src/androidTest/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootTest.kt @@ -0,0 +1,115 @@ +package com.reown.appkit.ui.components.internal.root + +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.components.internal.commons.TestTags +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.rememberTestNavController +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import org.junit.Rule +import org.junit.Test + +class AppKitRootTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun web3ModalRoot_shouldShowTitleFromEnumRoute() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + } + + composeTestRule.onNodeWithTag(TestTags.TITLE).assertExists() + composeTestRule.onNodeWithText(Route.CONNECT_YOUR_WALLET.title!!).assertExists() + } + + @Test + fun web3ModalRoot_shouldShowTitleFromNavArg() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + + LaunchedEffect(Unit) { + controller.navigate(Route.REDIRECT.path + "&" + "title") + } + } + + composeTestRule.onNodeWithTag("Title").assertExists() + composeTestRule.onNodeWithText("title").assertExists() + } + + @Test + fun web3ModalRoot_shouldShowWithoutTitle() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + + LaunchedEffect(Unit) { + controller.navigate("A") + } + } + + composeTestRule.onNodeWithTag("Title").assertDoesNotExist() + } + + @Test + fun web3ModalRoot_QuestionMarkIcon() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + } + + composeTestRule.onNodeWithContentDescription(ContentDescription.QUESTION_MARK.description) + } + + @Test + fun web3ModalRoot_BackIcon() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + } + + composeTestRule.onNodeWithContentDescription(ContentDescription.BACK_ARROW.description) + } + + @Test + fun web3ModalRoot_closeIsCalled() { + composeTestRule.setContent { + val controller = rememberTestNavController() + ProvideAppKitThemeComposition { + AppKitRoot(navController = controller, closeModal = { }) { + Box {} + } + } + } + + composeTestRule.onNodeWithContentDescription(ContentDescription.BACK_ARROW.description) + } +} \ No newline at end of file diff --git a/product/web3modal/src/main/AndroidManifest.xml b/product/appkit/src/main/AndroidManifest.xml similarity index 100% rename from product/web3modal/src/main/AndroidManifest.xml rename to product/appkit/src/main/AndroidManifest.xml diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/AppKit.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/AppKit.kt new file mode 100644 index 000000000..6b765d0f3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/AppKit.kt @@ -0,0 +1,297 @@ +package com.reown.appkit.client + +import androidx.activity.ComponentActivity +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Props +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.common.exceptions.SignClientAlreadyInitializedException +import com.reown.util.Empty +import com.reown.appkit.client.models.Account +import com.reown.appkit.client.models.Session +import com.reown.appkit.client.models.AppKitClientAlreadyInitializedException +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.client.models.request.SentRequestResult +import com.reown.appkit.di.appKitModule +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.domain.model.Session.WalletConnect +import com.reown.appkit.domain.model.toModalError +import com.reown.appkit.engine.AppKitEngine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.jetbrains.annotations.ApiStatus.Experimental +import org.koin.core.qualifier.named +import org.koin.dsl.module + +object AppKit { + + internal var chains: List = listOf() + + internal var sessionProperties: Map? = null + + internal var selectedChain: Modal.Model.Chain? = null + + internal var authPayloadParams: Modal.Model.AuthPayloadParams? = null + + private lateinit var appKitEngine: AppKitEngine + + interface ModalDelegate { + fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) + fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) + fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) + + @Deprecated( + message = "Use onSessionEvent(Modal.Model.Event) instead. Using both will result in duplicate events.", + replaceWith = ReplaceWith(expression = "onEvent(event)") + ) + fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) + fun onSessionEvent(sessionEvent: Modal.Model.Event) {} + fun onSessionExtend(session: Modal.Model.Session) + fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) + + //Responses + fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) + fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Modal.Model.SessionAuthenticateResponse) {} + fun onSIWEAuthenticationResponse(response: Modal.Model.SIWEAuthenticateResponse) {} + + // Utils + fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) + fun onRequestExpired(request: Modal.Model.ExpiredRequest) + fun onConnectionStateChange(state: Modal.Model.ConnectionState) + fun onError(error: Modal.Model.Error) + } + + interface ComponentDelegate { + fun onModalExpanded() + + fun onModalHidden() + + } + + fun initialize( + init: Modal.Params.Init, + onSuccess: () -> Unit = {}, + onError: (Modal.Model.Error) -> Unit, + ) { + SignClient.initialize( + init = Sign.Params.Init(init.core), + onSuccess = { + onInitializedClient(init, onSuccess, onError) + }, + onError = { error -> + if (error.throwable is SignClientAlreadyInitializedException) { + onInitializedClient(init, onSuccess, onError) + } else { + return@initialize onError(Modal.Model.Error(error.throwable)) + } + } + ) + } + + @Experimental + fun register(activity: ComponentActivity) { + checkEngineInitialization() + appKitEngine.registerCoinbaseLauncher(activity) + } + + @Experimental + fun unregister() { + checkEngineInitialization() + appKitEngine.unregisterCoinbase() + } + + @Throws(IllegalStateException::class) + private fun checkEngineInitialization() { + check(::appKitEngine.isInitialized) { + "AppKit needs to be initialized first using the initialize function" + } + } + + private fun onInitializedClient( + init: Modal.Params.Init, + onSuccess: () -> Unit = {}, + onError: (Modal.Model.Error) -> Unit, + ) { + if (!::appKitEngine.isInitialized) { + runCatching { + wcKoinApp.modules(appKitModule()) + appKitEngine = wcKoinApp.koin.get() + appKitEngine.setup(init, onError) + appKitEngine.setInternalDelegate(AppKitDelegate) + wcKoinApp.modules( + module { single(named(AndroidCommonDITags.ENABLE_WEB_3_MODAL_ANALYTICS)) { init.enableAnalytics ?: appKitEngine.fetchAnalyticsConfig() } } + ) + } + .onFailure { error -> return@onInitializedClient onError(Modal.Model.Error(error)) } + .onSuccess { + onSuccess() + appKitEngine.send(Props(event = EventType.TRACK, type = EventType.Track.MODAL_LOADED)) + } + } else { + onError(Modal.Model.Error(AppKitClientAlreadyInitializedException())) + } + } + + fun setChains(chains: List) { + this.chains = chains + } + + fun setAuthRequestParams(authParams: Modal.Model.AuthPayloadParams) { + authPayloadParams = authParams + } + + fun setSessionProperties(properties: Map) { + sessionProperties = properties + } + + @Throws(IllegalStateException::class) + fun setDelegate(delegate: ModalDelegate) { + AppKitDelegate.connectionState.onEach { connectionState -> + delegate.onConnectionStateChange(connectionState) + }.launchIn(scope) + + AppKitDelegate.wcEventModels.onEach { event -> + when (event) { + is Modal.Model.ApprovedSession -> delegate.onSessionApproved(event) + is Modal.Model.DeletedSession.Success -> delegate.onSessionDelete(event) + is Modal.Model.Error -> delegate.onError(event) + is Modal.Model.RejectedSession -> delegate.onSessionRejected(event) + is Modal.Model.Session -> delegate.onSessionExtend(event) + //todo: how to notify developer to not us both at the same time + is Modal.Model.SessionEvent -> delegate.onSessionEvent(event) + is Modal.Model.Event -> delegate.onSessionEvent(event) + is Modal.Model.SessionRequestResponse -> delegate.onSessionRequestResponse(event) + is Modal.Model.UpdatedSession -> delegate.onSessionUpdate(event) + is Modal.Model.ExpiredRequest -> delegate.onRequestExpired(event) + is Modal.Model.ExpiredProposal -> delegate.onProposalExpired(event) + is Modal.Model.SessionAuthenticateResponse -> delegate.onSessionAuthenticateResponse(event) + is Modal.Model.SIWEAuthenticateResponse -> delegate.onSIWEAuthenticationResponse(event) + else -> Unit + } + }.launchIn(scope) + } + + fun connect( + connect: Modal.Params.Connect, + onSuccess: (String) -> Unit, + onError: (Modal.Model.Error) -> Unit + ) { + SignClient.connect( + connect = connect.toSign(), + onSuccess = { url -> onSuccess(url) }, + onError = { onError(it.toModal()) } + ) + } + + fun authenticate( + authenticate: Modal.Params.Authenticate, + walletAppLink: String? = null, + onSuccess: (String) -> Unit, + onError: (Modal.Model.Error) -> Unit, + ) { + + SignClient.authenticate(authenticate.toSign(), walletAppLink, + onSuccess = { url -> onSuccess(url) }, + onError = { onError(it.toModal()) }) + } + + fun handleDeepLink(url: String, onError: (Modal.Model.Error) -> Unit) { + SignClient.dispatchEnvelope(url) { + onError(it.toModal()) + } + } + + fun request( + request: Request, + onSuccess: (SentRequestResult) -> Unit = {}, + onError: (Throwable) -> Unit, + ) { + checkEngineInitialization() + appKitEngine.request(request, onSuccess, onError) + } + + private fun SentRequestResult.sentRequestToModal() = when (this) { + is SentRequestResult.Coinbase -> Modal.Model.SentRequest(Long.MIN_VALUE, String.Empty, method, params, chainId) + is SentRequestResult.WalletConnect -> Modal.Model.SentRequest(requestId, sessionTopic, method, params, chainId) + } + + fun request( + request: Request, + onSuccess: () -> Unit, + onError: (Throwable) -> Unit, + ) { + checkEngineInitialization() + appKitEngine.request(request, { onSuccess() }, onError) + } + + fun ping(sessionPing: Modal.Listeners.SessionPing? = null) = appKitEngine.ping(sessionPing) + + fun disconnect( + onSuccess: () -> Unit, + onError: (Throwable) -> Unit, + ) { + checkEngineInitialization() + appKitEngine.disconnect(onSuccess, onError) + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getSelectedChain() = selectedChain + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Deprecated( + message = "Getting active session is replaced with getAccount()", + replaceWith = ReplaceWith("com.reown.appkit.client.AppKit.getAccount()"), + level = DeprecationLevel.WARNING + ) + internal fun getActiveSessionByTopic(topic: String) = SignClient.getActiveSessionByTopic(topic)?.toModal() + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Deprecated( + message = "Getting active session is replaced with getAccount()", + replaceWith = ReplaceWith("com.reown.appkit.client.AppKit.getAccount()"), + level = DeprecationLevel.WARNING + ) + fun getActiveSession(): Modal.Model.Session? { + checkEngineInitialization() + return (appKitEngine.getActiveSession() as? WalletConnect)?.topic?.let { SignClient.getActiveSessionByTopic(it)?.toModal() } + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getAccount(): Account? { + checkEngineInitialization() + return appKitEngine.getAccount() + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getSession(): Session? { + checkEngineInitialization() + return appKitEngine.getSession() + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getConnectorType(): Modal.ConnectorType? { + checkEngineInitialization() + return appKitEngine.getConnectorType() + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/ClientMapper.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/ClientMapper.kt new file mode 100644 index 000000000..5a9818a79 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/ClientMapper.kt @@ -0,0 +1,164 @@ +package com.reown.appkit.client + +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.sign.client.Sign +import com.reown.appkit.client.models.Account +import com.reown.appkit.client.models.Session +import com.reown.appkit.client.models.request.Request +import java.text.SimpleDateFormat +import java.util.Calendar + +internal fun Sign.Model.ApprovedSession.toModal() = Modal.Model.ApprovedSession.WalletConnectSession(topic, metaData, namespaces.toModal(), accounts) + +internal fun Map.toModal() = + mapValues { (_, namespace) -> Modal.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) } + +internal fun Sign.Model.RejectedSession.toModal() = Modal.Model.RejectedSession(topic, reason) + +internal fun Sign.Model.UpdatedSession.toModal() = Modal.Model.UpdatedSession(topic, namespaces.toModal()) + +internal fun Sign.Model.SessionEvent.toModal() = Modal.Model.SessionEvent(name, data) + +internal fun Sign.Model.Event.toModal() = Modal.Model.Event(topic, name, data, chainId) + +internal fun Sign.Model.Session.toModal() = Modal.Model.Session(pairingTopic, topic, expiry, namespaces.toModal(), metaData) + +internal fun Sign.Model.DeletedSession.toModal() = when (this) { + is Sign.Model.DeletedSession.Error -> Modal.Model.DeletedSession.Error(error) + is Sign.Model.DeletedSession.Success -> Modal.Model.DeletedSession.Success(topic, reason) +} + +internal fun Sign.Model.SessionRequestResponse.toModal() = Modal.Model.SessionRequestResponse(topic, chainId, method, result.toModal()) + +internal fun Sign.Model.JsonRpcResponse.toModal() = when (this) { + is Sign.Model.JsonRpcResponse.JsonRpcError -> Modal.Model.JsonRpcResponse.JsonRpcError(id, code, message) + is Sign.Model.JsonRpcResponse.JsonRpcResult -> Modal.Model.JsonRpcResponse.JsonRpcResult(id, result) +} + +@JvmSynthetic +internal fun Sign.Model.SessionAuthenticateResponse.toModal(): Modal.Model.SessionAuthenticateResponse = + when (this) { + is Sign.Model.SessionAuthenticateResponse.Result -> Modal.Model.SessionAuthenticateResponse.Result(id, cacaos.toClient(), session?.toModal()) + is Sign.Model.SessionAuthenticateResponse.Error -> Modal.Model.SessionAuthenticateResponse.Error(id, code, message) + } + +@JvmSynthetic +internal fun Sign.Model.ExpiredProposal.toModal(): Modal.Model.ExpiredProposal = Modal.Model.ExpiredProposal(pairingTopic, proposerPublicKey) + +@JvmSynthetic +internal fun Sign.Model.ExpiredRequest.toModal(): Modal.Model.ExpiredRequest = Modal.Model.ExpiredRequest(topic, id) + + +@JvmSynthetic +internal fun List.toClient(): List = this.map { + with(it) { + Modal.Model.Cacao( + Modal.Model.Cacao.Header(header.t), + Modal.Model.Cacao.Payload( + payload.iss, + payload.domain, + payload.aud, + payload.version, + payload.nonce, + payload.iat, + payload.nbf, + payload.exp, + payload.statement, + payload.requestId, + payload.resources + ), + Modal.Model.Cacao.Signature(signature.t, signature.s, signature.m) + ) + } +} + +internal fun Sign.Model.ConnectionState.toModal() = Modal.Model.ConnectionState(isAvailable) + +internal fun Sign.Model.Error.toModal() = Modal.Model.Error(throwable) + +internal fun Sign.Params.Disconnect.toModal() = Modal.Params.Disconnect(sessionTopic) + +internal fun Sign.Model.SentRequest.toModal() = Modal.Model.SentRequest(requestId, sessionTopic, method, params, chainId) + +internal fun Sign.Model.Ping.Success.toModal() = Modal.Model.Ping.Success(topic) + +internal fun Sign.Model.Ping.Error.toModal() = Modal.Model.Ping.Error(error) + +// toSign() +internal fun Modal.Params.Connect.toSign() = Sign.Params.Connect(namespaces?.toSign(), optionalNamespaces?.toSign(), properties, pairing) + +internal fun Modal.Params.Authenticate.toSign(): Sign.Params.Authenticate = with(this) { + Sign.Params.Authenticate( + pairingTopic, + chains = chains, + domain = domain, + uri = uri, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + methods = methods, + expiry = expiry + ) +} + +internal fun Modal.Model.AuthPayloadParams.toModel(pairingTopic: String): Modal.Params.Authenticate = with(this) { + Modal.Params.Authenticate( + chains = chains, + domain = domain, + uri = uri, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + expiry = expiry, + methods = methods, + pairingTopic = pairingTopic, + ) +} + +internal fun Modal.Model.AuthPayloadParams.toSign(issuer: String): Sign.Params.FormatMessage = with(this) { + Sign.Params.FormatMessage( + payloadParams = Sign.Model.PayloadParams( + chains = chains, + domain = domain, + aud = uri, + nonce = nonce, + nbf = nbf, + exp = exp, + iat = SimpleDateFormat(Cacao.Payload.ISO_8601_PATTERN).format(Calendar.getInstance().time), + type = "", + statement = statement, + requestId = requestId, + resources = resources, + ), + iss = issuer + ) +} + + +internal fun Map.toSign() = mapValues { (_, namespace) -> Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) } + +internal fun Modal.Params.Disconnect.toSign() = Sign.Params.Disconnect(sessionTopic) + +internal fun Modal.Params.Ping.toSign() = Sign.Params.Ping(topic) + +internal fun Request.toSign(sessionTopic: String, chainId: String) = Sign.Params.Request(sessionTopic, method, params, chainId, expiry) + +internal fun Modal.Listeners.SessionPing.toSign() = object : Sign.Listeners.SessionPing { + override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { + this@toSign.onSuccess(pingSuccess.toModal()) + } + + override fun onError(pingError: Sign.Model.Ping.Error) { + this@toSign.onError(pingError.toModal()) + } +} + +internal fun Sign.Model.Session.toSession() = Session.WalletConnectSession(pairingTopic, topic, expiry, namespaces.toModal(), metaData) + +internal fun Account.toCoinbaseSession() = Session.CoinbaseSession(chain.id, address) \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/Modal.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/Modal.kt new file mode 100644 index 000000000..57e285117 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/Modal.kt @@ -0,0 +1,272 @@ +package com.reown.appkit.client + +import androidx.annotation.DrawableRes +import androidx.annotation.Keep +import com.reown.android.Core +import com.reown.android.CoreInterface +import com.reown.android.cacao.SignatureInterface +import com.reown.android.internal.common.signing.cacao.Issuer + +object Modal { + + sealed interface Listeners { + interface SessionPing : Listeners { + fun onSuccess(pingSuccess: Model.Ping.Success) + fun onError(pingError: Model.Ping.Error) + } + } + + sealed class Params { + data class Init( + val core: CoreInterface, + val excludedWalletIds: List = listOf(), + val recommendedWalletsIds: List = listOf(), + val coinbaseEnabled: Boolean = true, + val enableAnalytics: Boolean? = null + ) : Params() + + data class Connect( + val namespaces: Map? = null, + val optionalNamespaces: Map? = null, + val properties: Map? = null, + val pairing: Core.Model.Pairing, + ) : Params() + + data class Authenticate( + val pairingTopic: String? = null, + val chains: List, + val domain: String, + val nonce: String, + val uri: String, + val nbf: String? = null, + val exp: String? = null, + val statement: String? = null, + val requestId: String? = null, + val resources: List? = null, + val methods: List? = null, + val expiry: Long? = null + ) : Params() + + @Deprecated( + message = "This has become deprecate in favor of the parameterless disconnect function", + level = DeprecationLevel.WARNING + ) + data class Disconnect(val sessionTopic: String) : Params() + + data class Ping(val topic: String) : Params() + + @Deprecated( + message = "Converted to sealed class to support multiple connectors", + replaceWith = ReplaceWith("com.reown.appkit.client.models.Request"), + level = DeprecationLevel.WARNING + ) + data class Request( + val method: String, + val params: String, + val expiry: Long? = null, + ) : Params() + + data class SessionParams( + val requiredNamespaces: Map, + val optionalNamespaces: Map? = null, + val properties: Map? = null + ) + } + + sealed class Model { + data class Error(val throwable: Throwable) : Model() + + sealed class Namespace : Model() { + data class Proposal( + val chains: List? = null, + val methods: List, + val events: List, + ) : Namespace() + + data class Session( + val chains: List? = null, + val accounts: List, + val methods: List, + val events: List, + ) : Namespace() + } + + data class AuthPayloadParams( + val chains: List, + val domain: String, + val nonce: String, + val uri: String, + val nbf: String? = null, + val exp: String? = null, + val statement: String? = null, + val requestId: String? = null, + val resources: List? = null, + val methods: List? = null, + val expiry: Long? = null + ) : Model() + + sealed class ApprovedSession : Model() { + data class WalletConnectSession( + val topic: String, + val metaData: Core.Model.AppMetaData?, + val namespaces: Map, + val accounts: List, + ) : ApprovedSession() + + data class CoinbaseSession( + val chain: String, + val networkId: String, + val address: String + ) : ApprovedSession() + } + + data class RejectedSession(val topic: String, val reason: String) : Model() + + data class UpdatedSession( + val topic: String, + val namespaces: Map, + ) : Model() + + data class SessionEvent( + val name: String, + val data: String, + ) : Model() + + data class Event( + val topic: String, + val name: String, + val data: String, + val chainId: String, + ) : Model() + + sealed class DeletedSession : Model() { + data class Success(val topic: String, val reason: String) : DeletedSession() + data class Error(val error: Throwable) : DeletedSession() + } + + data class Session( + val pairingTopic: String, + val topic: String, + val expiry: Long, + val namespaces: Map, + val metaData: Core.Model.AppMetaData?, + ) : Model() { + val redirect: String? = metaData?.redirect + } + + data class SessionRequestResponse( + val topic: String, + val chainId: String?, + val method: String, + val result: JsonRpcResponse, + ) : Model() + + data class ConnectionState( + val isAvailable: Boolean, + ) : Model() + + data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : Model() + data class ExpiredRequest(val topic: String, val id: Long) : Model() + + sealed class JsonRpcResponse : Model() { + abstract val id: Long + val jsonrpc: String = "2.0" + + data class JsonRpcResult( + override val id: Long, + val result: String, + ) : JsonRpcResponse() + + data class JsonRpcError( + override val id: Long, + val code: Int, + val message: String, + ) : JsonRpcResponse() + } + + @Deprecated( + message = "Converted to sealed class to support multiple connectors", + replaceWith = ReplaceWith("com.reown.appkit.client.models.SentRequestResult"), + level = DeprecationLevel.WARNING + ) + data class SentRequest( + val requestId: Long, + val sessionTopic: String, + val method: String, + val params: String, + val chainId: String, + ) : Model() + + sealed class SessionAuthenticateResponse : Model() { + data class Result(val id: Long, val cacaos: List, val session: Session?) : SessionAuthenticateResponse() + data class Error(val id: Long, val code: Int, val message: String) : SessionAuthenticateResponse() + } + + sealed class SIWEAuthenticateResponse : Model() { + data class Result(val id: Long, val message: String, val signature: String) : SIWEAuthenticateResponse() + data class Error(val id: Long, val code: Int, val message: String) : SIWEAuthenticateResponse() + } + + data class Cacao( + val header: Header, + val payload: Payload, + val signature: Signature, + ) : Model() { + @Keep + data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface + data class Header(val t: String) : Model() + data class Payload( + val iss: String, + val domain: String, + val aud: String, + val version: String, + val nonce: String, + val iat: String, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + val resources: List?, + ) : Model() { + val address: String get() = Issuer(iss).address + } + } + + sealed class Ping : Model() { + data class Success(val topic: String) : Ping() + data class Error(val error: Throwable) : Ping() + } + + data class Chain( + val chainName: String, + val chainNamespace: String, + val chainReference: String, + val requiredMethods: List, + val optionalMethods: List, + val events: List, + val token: Token, + val chainImage: ChainImage? = null, + val rpcUrl: String? = null, + val blockExplorerUrl: String? = null + ) { + val id: String = "$chainNamespace:$chainReference" + } + + sealed class ChainImage { + data class Asset(@DrawableRes val id: Int) : ChainImage() + + data class Network(val url: String) : ChainImage() + } + + data class Token( + val name: String, + val symbol: String, + val decimal: Int + ) + } + + enum class ConnectorType { + WALLET_CONNECT, + COINBASE + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Account.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Account.kt new file mode 100644 index 000000000..958cbee42 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Account.kt @@ -0,0 +1,8 @@ +package com.reown.appkit.client.models + +import com.reown.appkit.client.Modal + +data class Account( + val address: String, + val chain: Modal.Model.Chain +) \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Exceptions.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Exceptions.kt new file mode 100644 index 000000000..0d2bd34a9 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Exceptions.kt @@ -0,0 +1,6 @@ +package com.reown.appkit.client.models + +import com.reown.android.internal.common.exception.WalletConnectException + +class AppKitClientAlreadyInitializedException : WalletConnectException("AppKit already initialized") +class CoinbaseClientAlreadyInitializedException : WalletConnectException("Coinbase already initialized") \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Session.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Session.kt new file mode 100644 index 000000000..707d9b88d --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/Session.kt @@ -0,0 +1,22 @@ +package com.reown.appkit.client.models + +import com.reown.android.Core +import com.reown.appkit.client.Modal + +sealed class Session { + + data class WalletConnectSession( + val pairingTopic: String, + val topic: String, + val expiry: Long, + val namespaces: Map, + val metaData: Core.Model.AppMetaData?, + ) : Session() { + val redirect: String? = metaData?.redirect + } + + data class CoinbaseSession( + val chain: String, + val address: String + ) : Session() +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/Request.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/Request.kt new file mode 100644 index 000000000..cd2943244 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/Request.kt @@ -0,0 +1,7 @@ +package com.reown.appkit.client.models.request + +data class Request( + val method: String, + val params: String, + val expiry: Long? = null, +) \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/SentRequestResult.kt b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/SentRequestResult.kt similarity index 80% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/SentRequestResult.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/SentRequestResult.kt index 2ce7dd9f6..466fde7e3 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/SentRequestResult.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/client/models/request/SentRequestResult.kt @@ -1,7 +1,7 @@ -package com.walletconnect.web3.modal.client.models.request +package com.reown.appkit.client.models.request -import com.walletconnect.sign.client.Sign -import com.walletconnect.web3.modal.engine.coinbase.CoinbaseResult +import com.reown.sign.client.Sign +import com.reown.appkit.engine.coinbase.CoinbaseResult sealed class SentRequestResult { abstract val method: String diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/data/BalanceRpcRepository.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/BalanceRpcRepository.kt new file mode 100644 index 000000000..bd312faf5 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/BalanceRpcRepository.kt @@ -0,0 +1,32 @@ +package com.reown.appkit.data + +import com.reown.foundation.util.Logger +import com.reown.appkit.client.Modal +import com.reown.appkit.data.json_rpc.balance.BalanceRequest +import com.reown.appkit.data.json_rpc.balance.BalanceRpcResponse +import com.reown.appkit.data.network.BalanceService +import com.reown.appkit.domain.model.Balance + +internal class BalanceRpcRepository( + private val balanceService: BalanceService, + private val logger: Logger, +) { + + suspend fun getBalance( + token: Modal.Model.Token, rpcUrl: String, address: String + ) = runCatching { + balanceService.getBalance( + url = rpcUrl, body = BalanceRequest(address = address) + ) + }.mapCatching { response -> + response.body()!!.mapResponse(token) + }.onFailure { + logger.error(it) + }.getOrNull() +} + +private fun BalanceRpcResponse.mapResponse(token: Modal.Model.Token) = when { + result != null -> Balance(token, result) + error != null -> throw Throwable(error.message) + else -> throw Throwable("Invalid balance response") +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/data/BlockchainRepository.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/BlockchainRepository.kt new file mode 100644 index 000000000..d1e8e9127 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/BlockchainRepository.kt @@ -0,0 +1,24 @@ +package com.reown.appkit.data + +import com.reown.android.internal.common.model.ProjectId +import com.reown.appkit.data.model.IdentityDTO +import com.reown.appkit.data.network.BlockchainService +import com.reown.appkit.domain.model.Identity + +internal class BlockchainRepository( + private val blockchainService: BlockchainService, + private val projectId: ProjectId +) { + + suspend fun getIdentity(address: String, chainId: String) = with( + blockchainService.getIdentity(address = address, chainId = chainId, projectId = projectId.value) + ) { + if (isSuccessful && body() != null) { + body()!!.toIdentity() + } else { + throw Throwable(errorBody()?.string()) + } + } +} + +private fun IdentityDTO.toIdentity() = Identity(name, avatar) diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/JsonRpcMethod.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/JsonRpcMethod.kt new file mode 100644 index 000000000..44978ddce --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/JsonRpcMethod.kt @@ -0,0 +1,6 @@ +package com.reown.appkit.data.json_rpc + +internal object JsonRpcMethod { + @get:JvmSynthetic + const val ETH_GET_BALANCE: String = "eth_getBalance" +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpc.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpc.kt new file mode 100644 index 000000000..07ac79876 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpc.kt @@ -0,0 +1,19 @@ +package com.reown.appkit.data.json_rpc.balance + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.util.generateId +import com.reown.appkit.data.json_rpc.JsonRpcMethod + +@JsonClass(generateAdapter = true) +internal data class BalanceRequest( + val address: String, + @Json(name = "id") + val id: Long = generateId(), + @Json(name = "jsonrpc") + val jsonrpc: String = "2.0", + @Json(name = "method") + val method: String = JsonRpcMethod.ETH_GET_BALANCE, + @Json(name = "params") + val params: List = listOf(address, "latest") +) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpcResponse.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpcResponse.kt similarity index 89% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpcResponse.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpcResponse.kt index bf5217979..627bf85c3 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpcResponse.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/json_rpc/balance/BalanceRpcResponse.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.data.json_rpc.balance +package com.reown.appkit.data.json_rpc.balance import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/model/IdentityDTO.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/model/IdentityDTO.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/model/IdentityDTO.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/data/model/IdentityDTO.kt index 6b8a40805..84e41e47f 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/model/IdentityDTO.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/model/IdentityDTO.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.data.model +package com.reown.appkit.data.model import com.squareup.moshi.Json diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/data/network/BalanceService.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/network/BalanceService.kt new file mode 100644 index 000000000..856e64dda --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/network/BalanceService.kt @@ -0,0 +1,17 @@ +package com.reown.appkit.data.network + +import com.reown.appkit.data.json_rpc.balance.BalanceRequest +import com.reown.appkit.data.json_rpc.balance.BalanceRpcResponse +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Url + +internal interface BalanceService { + + @POST + suspend fun getBalance( + @Url url: String, + @Body body: BalanceRequest + ): Response +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BlockchainService.kt b/product/appkit/src/main/kotlin/com/reown/appkit/data/network/BlockchainService.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BlockchainService.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/data/network/BlockchainService.kt index 48cca5788..6f56860b1 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BlockchainService.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/data/network/BlockchainService.kt @@ -1,6 +1,6 @@ -package com.walletconnect.web3.modal.data.network +package com.reown.appkit.data.network -import com.walletconnect.web3.modal.data.model.IdentityDTO +import com.reown.appkit.data.model.IdentityDTO import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/di/AppKitDITags.kt b/product/appkit/src/main/kotlin/com/reown/appkit/di/AppKitDITags.kt new file mode 100644 index 000000000..2df13c1e9 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/di/AppKitDITags.kt @@ -0,0 +1,8 @@ +package com.reown.appkit.di + +internal enum class AppKitDITags { + BALANCE_RPC_RETROFIT, + BLOCKCHAIN_RETROFIT, + MOSHI, + SESSION_DATA_STORE, +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/di/BalanceRpcModule.kt b/product/appkit/src/main/kotlin/com/reown/appkit/di/BalanceRpcModule.kt new file mode 100644 index 000000000..7e0d8d8c1 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/di/BalanceRpcModule.kt @@ -0,0 +1,28 @@ +package com.reown.appkit.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.appkit.data.BalanceRpcRepository +import com.reown.appkit.data.network.BalanceService +import com.reown.appkit.domain.usecase.GetEthBalanceUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory + +internal fun balanceRpcModule() = module { + + single(named(AppKitDITags.BALANCE_RPC_RETROFIT)) { + Retrofit.Builder() + // Passing url to google to passing retrofit verification. The correct url to chain RPC is provided on the BalanceService::class + .baseUrl("https://google.com/") + .client(get(named(AndroidCommonDITags.OK_HTTP))) + .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) + .build() + } + + single { get(named(AppKitDITags.BALANCE_RPC_RETROFIT)).create(BalanceService::class.java) } + + single { BalanceRpcRepository(balanceService = get(), logger = get()) } + + single { GetEthBalanceUseCase(balanceRpcRepository = get()) } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/di/BlockchainApiModule.kt b/product/appkit/src/main/kotlin/com/reown/appkit/di/BlockchainApiModule.kt new file mode 100644 index 000000000..9a8035f18 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/di/BlockchainApiModule.kt @@ -0,0 +1,31 @@ +package com.reown.appkit.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.appkit.data.BlockchainRepository +import com.reown.appkit.data.network.BlockchainService +import com.reown.appkit.domain.usecase.GetIdentityUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory + +internal fun blockchainApiModule() = module { + + single(named(AppKitDITags.BLOCKCHAIN_RETROFIT)) { + Retrofit.Builder() + .baseUrl("https://rpc.walletconnect.org/v1/") + .client(get(named(AndroidCommonDITags.OK_HTTP))) + .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) + .build() + } + + single { + get(named(AppKitDITags.BLOCKCHAIN_RETROFIT)).create(BlockchainService::class.java) + } + + single { + BlockchainRepository(blockchainService = get(), projectId = get()) + } + + single { GetIdentityUseCase(blockchainRepository = get()) } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/di/EngineModule.kt b/product/appkit/src/main/kotlin/com/reown/appkit/di/EngineModule.kt new file mode 100644 index 000000000..216a5da30 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/di/EngineModule.kt @@ -0,0 +1,36 @@ +package com.reown.appkit.di + +import android.content.Context +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.appkit.domain.usecase.ConnectionEventRepository +import com.reown.appkit.engine.AppKitEngine +import com.reown.appkit.engine.coinbase.CoinbaseClient +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module + +internal fun engineModule() = module { + + single { + ConnectionEventRepository(sharedPreferences = androidContext().getSharedPreferences("ConnectionEvents", Context.MODE_PRIVATE)) + } + + single { + AppKitEngine( + getSessionUseCase = get(), + getSelectedChainUseCase = get(), + deleteSessionDataUseCase = get(), + saveSessionUseCase = get(), + connectionEventRepository = get(), + enableAnalyticsUseCase = get(), + sendEventUseCase = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + ) + } + single { + CoinbaseClient( + context = get(), + appMetaData = get() + ) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/di/Web3ModalModule.kt b/product/appkit/src/main/kotlin/com/reown/appkit/di/Web3ModalModule.kt new file mode 100644 index 000000000..ed7f08f89 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/di/Web3ModalModule.kt @@ -0,0 +1,59 @@ +package com.reown.appkit.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStore +import com.squareup.moshi.Moshi +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.appkit.domain.RecentWalletsRepository +import com.reown.appkit.domain.SessionRepository +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.DeleteSessionDataUseCase +import com.reown.appkit.domain.usecase.GetRecentWalletUseCase +import com.reown.appkit.domain.usecase.GetSelectedChainUseCase +import com.reown.appkit.domain.usecase.GetSessionUseCase +import com.reown.appkit.domain.usecase.ObserveSelectedChainUseCase +import com.reown.appkit.domain.usecase.ObserveSessionUseCase +import com.reown.appkit.domain.usecase.SaveChainSelectionUseCase +import com.reown.appkit.domain.usecase.SaveRecentWalletUseCase +import com.reown.appkit.domain.usecase.SaveSessionUseCase +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module + +private val Context.sessionDataStore: DataStore by preferencesDataStore(name = "session_store") + +internal fun appKitModule() = module { + + single { RecentWalletsRepository(sharedPreferences = get()) } + + single { GetRecentWalletUseCase(repository = get()) } + single { SaveRecentWalletUseCase(repository = get()) } + + single> { + PolymorphicJsonAdapterFactory.of(Session::class.java, "type") + .withSubtype(Session.WalletConnect::class.java, "wcsession") + .withSubtype(Session.Coinbase::class.java, "coinbase") + } + + single(named(AppKitDITags.MOSHI)) { + get(named(AndroidCommonDITags.MOSHI)) + .add(get>()) + .build() + } + + single(named(AppKitDITags.SESSION_DATA_STORE)) { androidContext().sessionDataStore } + single { SessionRepository(sessionStore = get(named(AppKitDITags.SESSION_DATA_STORE)), moshi = get(named(AppKitDITags.MOSHI))) } + + single { GetSessionUseCase(repository = get()) } + single { SaveSessionUseCase(repository = get()) } + single { DeleteSessionDataUseCase(repository = get()) } + single { SaveChainSelectionUseCase(repository = get()) } + single { GetSelectedChainUseCase(repository = get()) } + single { ObserveSessionUseCase(repository = get()) } + single { ObserveSelectedChainUseCase(repository = get()) } + + includes(blockchainApiModule(), balanceRpcModule(), engineModule()) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/RecentWalletsRepository.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/RecentWalletsRepository.kt new file mode 100644 index 000000000..412dd9f93 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/RecentWalletsRepository.kt @@ -0,0 +1,14 @@ +package com.reown.appkit.domain + +import android.content.SharedPreferences + +private const val RECENT_WALLET_ID = "w3m_recent_wallet_id" + +internal class RecentWalletsRepository( + private val sharedPreferences: SharedPreferences +) { + + fun saveRecentWalletId(id: String) = sharedPreferences.edit().putString(RECENT_WALLET_ID, id).apply() + + fun getRecentWalletId() = sharedPreferences.getString(RECENT_WALLET_ID, null) +} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/SessionRepository.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/SessionRepository.kt similarity index 86% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/SessionRepository.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/domain/SessionRepository.kt index c2054f649..5d7d90027 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/SessionRepository.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/SessionRepository.kt @@ -1,12 +1,12 @@ -package com.walletconnect.web3.modal.domain +package com.reown.appkit.domain import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import com.squareup.moshi.Moshi -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.model.Session +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.model.Session import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -41,7 +41,7 @@ internal class SessionRepository( is Session.WalletConnect -> session.copy(chain = chain) null -> null } - Web3Modal.selectedChain = Web3Modal.chains.find { it.id == chain } + AppKit.selectedChain = AppKit.chains.find { it.id == chain } store[SESSION] = adapter.toJson(updatedSession) } } diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/delegate/AppKitDelegate.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/delegate/AppKitDelegate.kt new file mode 100644 index 000000000..876a15f4c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/delegate/AppKitDelegate.kt @@ -0,0 +1,181 @@ +package com.reown.appkit.domain.delegate + +import com.reown.android.internal.common.wcKoinApp +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.DeleteSessionDataUseCase +import com.reown.appkit.domain.usecase.SaveChainSelectionUseCase +import com.reown.appkit.domain.usecase.SaveSessionUseCase +import com.reown.appkit.utils.EthUtils +import com.reown.appkit.utils.getAddress +import com.reown.appkit.utils.getSelectedChain +import com.reown.appkit.utils.toSession +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch + +internal object AppKitDelegate : AppKit.ModalDelegate { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() + val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() + + private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) + val connectionState: SharedFlow = _connectionState.asSharedFlow() + + //todo replace it with engine + private val saveSessionUseCase: SaveSessionUseCase by lazy { wcKoinApp.koin.get() } + private val saveChainSelectionUseCase: SaveChainSelectionUseCase by lazy { wcKoinApp.koin.get() } + private val deleteSessionDataUseCase: DeleteSessionDataUseCase by lazy { wcKoinApp.koin.get() } + + fun emit(event: Modal.Model?) { + scope.launch { + _wcEventModels.emit(event) + } + } + + override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { + scope.launch { + val chain = AppKit.chains.getSelectedChain(AppKit.selectedChain?.id) + saveSessionUseCase(approvedSession.toSession(chain)) + _wcEventModels.emit(approvedSession) + } + } + + override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Modal.Model.SessionAuthenticateResponse) { + scope.launch { + if (sessionAuthenticateResponse is Modal.Model.SessionAuthenticateResponse.Result) { + val chain = AppKit.chains.getSelectedChain(AppKit.selectedChain?.id) + saveSessionUseCase( + Session.WalletConnect( + chain = chain.id, + topic = sessionAuthenticateResponse.session?.topic ?: "", + address = sessionAuthenticateResponse.session?.getAddress(chain) ?: "" + ) + ) + } + + _wcEventModels.emit(sessionAuthenticateResponse) + } + } + + override fun onSIWEAuthenticationResponse(response: Modal.Model.SIWEAuthenticateResponse) { + scope.launch { + _wcEventModels.emit(response) + } + } + + override fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) { + scope.launch { + _wcEventModels.emit(rejectedSession) + } + } + + override fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) { + scope.launch { + val chain = AppKit.chains.getSelectedChain(AppKit.selectedChain?.id) + saveSessionUseCase(updatedSession.toSession(chain)) + _wcEventModels.emit(updatedSession) + } + } + + override fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) { + scope.launch { + consumeSessionEvent(sessionEvent) + _wcEventModels.emit(sessionEvent) + } + } + + override fun onSessionEvent(sessionEvent: Modal.Model.Event) { + scope.launch { + consumeEvent(sessionEvent) + _wcEventModels.emit(sessionEvent) + } + } + + private suspend fun consumeSessionEvent(sessionEvent: Modal.Model.SessionEvent) { + try { + when (sessionEvent.name) { + EthUtils.accountsChanged -> { + val (_, chainReference, _) = sessionEvent.data.split(":") + AppKit.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } + } + + EthUtils.chainChanged -> { + val (chainReference, _) = sessionEvent.data.split(".") + AppKit.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } + } + } + } catch (throwable: Throwable) { + onError(Modal.Model.Error(throwable)) + } + } + + //todo: Do we need to change anything here? We have more data in event now. + private suspend fun consumeEvent(event: Modal.Model.Event) { + try { + when (event.name) { + EthUtils.accountsChanged -> { + //todo: Can we take chainReference from the event? + val (_, chainReference, _) = event.data.split(":") + AppKit.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } + } + + EthUtils.chainChanged -> { + //todo: Can we take chainReference from the event? + val (chainReference, _) = event.data.split(".") + AppKit.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } + } + } + } catch (throwable: Throwable) { + onError(Modal.Model.Error(throwable)) + } + } + + override fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) { + scope.launch { + deleteSessionDataUseCase() + _wcEventModels.emit(deletedSession) + } + } + + override fun onSessionExtend(session: Modal.Model.Session) { + scope.launch { + _wcEventModels.emit(session) + } + } + + override fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) { + scope.launch { + _wcEventModels.emit(response) + } + } + + override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) { + scope.launch { + _wcEventModels.emit(proposal) + } + } + + override fun onRequestExpired(request: Modal.Model.ExpiredRequest) { + scope.launch { + _wcEventModels.emit(request) + } + } + + override fun onConnectionStateChange(state: Modal.Model.ConnectionState) { + scope.launch { + _connectionState.emit(state) + } + } + + override fun onError(error: Modal.Model.Error) { + scope.launch { + _wcEventModels.emit(error) + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/AccountData.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/AccountData.kt new file mode 100644 index 000000000..304188a9a --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/AccountData.kt @@ -0,0 +1,9 @@ +package com.reown.appkit.domain.model + +import com.reown.appkit.client.Modal + +internal data class AccountData( + val address: String, + val chains: List, + val identity: Identity? = null +) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Balance.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Balance.kt similarity index 85% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Balance.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Balance.kt index f1d19a981..3fbc9983f 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Balance.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Balance.kt @@ -1,6 +1,6 @@ -package com.walletconnect.web3.modal.domain.model +package com.reown.appkit.domain.model -import com.walletconnect.web3.modal.client.Modal +import com.reown.appkit.client.Modal import java.math.BigDecimal internal data class Balance( diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Exceptions.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Exceptions.kt new file mode 100644 index 000000000..221c2a4b0 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Exceptions.kt @@ -0,0 +1,7 @@ +package com.reown.appkit.domain.model + +import com.reown.appkit.client.Modal + +object InvalidSessionException: Throwable("Session topic is missing") + +internal fun Throwable.toModalError() = Modal.Model.Error(this) \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Identity.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Identity.kt new file mode 100644 index 000000000..04275c602 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Identity.kt @@ -0,0 +1,6 @@ +package com.reown.appkit.domain.model + +data class Identity( + val name: String? = null, + val avatar: String? = null +) diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Session.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Session.kt new file mode 100644 index 000000000..b23c2a4b9 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/model/Session.kt @@ -0,0 +1,21 @@ +package com.reown.appkit.domain.model + +import com.squareup.moshi.JsonClass + +internal sealed class Session { + abstract val address: String + abstract val chain: String + + @JsonClass(generateAdapter = true) + data class WalletConnect( + override val address: String, + override val chain: String, + val topic: String, + ) : Session() + + @JsonClass(generateAdapter = true) + data class Coinbase( + override val chain: String, + override val address: String, + ) : Session() +} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ConnectionEventRepository.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ConnectionEventRepository.kt similarity index 94% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ConnectionEventRepository.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ConnectionEventRepository.kt index 7cf2b616f..39ee854f3 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ConnectionEventRepository.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ConnectionEventRepository.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.domain.usecase +package com.reown.appkit.domain.usecase import android.content.SharedPreferences diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/DeleteSessionDataUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/DeleteSessionDataUseCase.kt new file mode 100644 index 000000000..98a22c683 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/DeleteSessionDataUseCase.kt @@ -0,0 +1,11 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository + +internal class DeleteSessionDataUseCase( + private val repository: SessionRepository +) { + suspend operator fun invoke() { + repository.deleteSession() + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetEthBalanceUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetEthBalanceUseCase.kt new file mode 100644 index 000000000..deb47babc --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetEthBalanceUseCase.kt @@ -0,0 +1,18 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.client.Modal +import com.reown.appkit.data.BalanceRpcRepository + +internal class GetEthBalanceUseCase( + private val balanceRpcRepository: BalanceRpcRepository, +) { + suspend operator fun invoke( + token: Modal.Model.Token, + rpcUrl: String, + address: String + ) = balanceRpcRepository.getBalance( + token = token, + rpcUrl = rpcUrl, + address = address + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetIdentityUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetIdentityUseCase.kt new file mode 100644 index 000000000..aeb005a62 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetIdentityUseCase.kt @@ -0,0 +1,14 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.data.BlockchainRepository +import com.reown.appkit.domain.model.Identity + +internal class GetIdentityUseCase( + private val blockchainRepository: BlockchainRepository +) { + suspend operator fun invoke(address: String, chainId: String) = try { + blockchainRepository.getIdentity(address = address, chainId = chainId) + } catch (e: Throwable) { + null + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetRecentWalletUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetRecentWalletUseCase.kt new file mode 100644 index 000000000..2a64af3b2 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetRecentWalletUseCase.kt @@ -0,0 +1,7 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.RecentWalletsRepository + +internal class GetRecentWalletUseCase(private val repository: RecentWalletsRepository) { + operator fun invoke() = repository.getRecentWalletId() +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSelectedChainUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSelectedChainUseCase.kt new file mode 100644 index 000000000..5d080b87e --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSelectedChainUseCase.kt @@ -0,0 +1,11 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking + +internal class GetSelectedChainUseCase( + private val repository: SessionRepository +) { + operator fun invoke() = runBlocking { repository.session.first()?.chain } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSessionUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSessionUseCase.kt new file mode 100644 index 000000000..24a244e8c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/GetSessionUseCase.kt @@ -0,0 +1,10 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository +import kotlinx.coroutines.runBlocking + +internal class GetSessionUseCase( + private val repository: SessionRepository +) { + operator fun invoke() = runBlocking { repository.getSession() } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSelectedChainUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSelectedChainUseCase.kt new file mode 100644 index 000000000..cee459df9 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSelectedChainUseCase.kt @@ -0,0 +1,10 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository +import kotlinx.coroutines.flow.map + +internal class ObserveSelectedChainUseCase( + private val repository: SessionRepository +) { + operator fun invoke() = repository.session.map { it?.chain } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSessionUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSessionUseCase.kt new file mode 100644 index 000000000..63dbcb2ae --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/ObserveSessionUseCase.kt @@ -0,0 +1,9 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository + +internal class ObserveSessionUseCase( + private val repository: SessionRepository +) { + operator fun invoke() = repository.session +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveChainSelectionUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveChainSelectionUseCase.kt new file mode 100644 index 000000000..870985bb7 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveChainSelectionUseCase.kt @@ -0,0 +1,11 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository + +internal class SaveChainSelectionUseCase( + private val repository: SessionRepository +) { + suspend operator fun invoke(chain: String) { + repository.updateChainSelection(chain) + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveRecentWalletUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveRecentWalletUseCase.kt new file mode 100644 index 000000000..e5163c237 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveRecentWalletUseCase.kt @@ -0,0 +1,9 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.RecentWalletsRepository + +internal class SaveRecentWalletUseCase( + private val repository: RecentWalletsRepository +) { + operator fun invoke(id: String) = repository.saveRecentWalletId(id) +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveSessionUseCase.kt b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveSessionUseCase.kt new file mode 100644 index 000000000..43c447172 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/domain/usecase/SaveSessionUseCase.kt @@ -0,0 +1,10 @@ +package com.reown.appkit.domain.usecase + +import com.reown.appkit.domain.SessionRepository +import com.reown.appkit.domain.model.Session + +internal class SaveSessionUseCase( + private val repository: SessionRepository +) { + suspend operator fun invoke(session: Session) = repository.saveSession(session) +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/engine/AppKitEngine.kt b/product/appkit/src/main/kotlin/com/reown/appkit/engine/AppKitEngine.kt new file mode 100644 index 000000000..46b3c16c1 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/engine/AppKitEngine.kt @@ -0,0 +1,353 @@ +package com.reown.appkit.engine + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import com.reown.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCaseInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.util.Empty +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.models.Account +import com.reown.appkit.client.models.CoinbaseClientAlreadyInitializedException +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.client.models.request.SentRequestResult +import com.reown.appkit.client.models.request.toSentRequest +import com.reown.appkit.client.toCoinbaseSession +import com.reown.appkit.client.toModal +import com.reown.appkit.client.toSession +import com.reown.appkit.client.toSign +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.domain.model.InvalidSessionException +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.ConnectionEventRepository +import com.reown.appkit.domain.usecase.DeleteSessionDataUseCase +import com.reown.appkit.domain.usecase.GetSelectedChainUseCase +import com.reown.appkit.domain.usecase.GetSessionUseCase +import com.reown.appkit.domain.usecase.SaveSessionUseCase +import com.reown.appkit.engine.coinbase.COINBASE_WALLET_ID +import com.reown.appkit.engine.coinbase.CoinbaseClient +import com.reown.appkit.utils.getSelectedChain +import com.reown.appkit.utils.toAccount +import com.reown.appkit.utils.toChain +import com.reown.appkit.utils.toConnectorType +import com.reown.appkit.utils.toSession +import kotlinx.coroutines.launch + +internal class AppKitEngine( + private val getSessionUseCase: GetSessionUseCase, + private val getSelectedChainUseCase: GetSelectedChainUseCase, + private val saveSessionUseCase: SaveSessionUseCase, + private val deleteSessionDataUseCase: DeleteSessionDataUseCase, + private val sendEventUseCase: SendEventInterface, + private val connectionEventRepository: ConnectionEventRepository, + private val enableAnalyticsUseCase: EnableAnalyticsUseCaseInterface, + private val logger: Logger +) : SendEventInterface by sendEventUseCase, + EnableAnalyticsUseCaseInterface by enableAnalyticsUseCase { + internal var excludedWalletsIds: MutableList = mutableListOf() + internal var recommendedWalletsIds: MutableList = mutableListOf() + internal var siweRequestIdWithMessage: Pair? = null + private lateinit var coinbaseClient: CoinbaseClient + internal var shouldDisconnect: Boolean = true + + fun setup( + init: Modal.Params.Init, + onError: (Modal.Model.Error) -> Unit + ) { + excludedWalletsIds.addAll(init.excludedWalletIds) + recommendedWalletsIds.addAll(init.recommendedWalletsIds) + setupCoinbase(init, onError) + } + + private fun setupCoinbase(init: Modal.Params.Init, onError: (Modal.Model.Error) -> Unit) { + if (init.coinbaseEnabled) { + if (!::coinbaseClient.isInitialized) { + coinbaseClient = CoinbaseClient(context = wcKoinApp.koin.get(), appMetaData = wcKoinApp.koin.get()) + } else { + onError(Modal.Model.Error(CoinbaseClientAlreadyInitializedException())) + } + } else { + excludedWalletsIds.add(COINBASE_WALLET_ID) + } + } + + fun connectWC( + name: String, method: String, + connect: Modal.Params.Connect, + onSuccess: (String) -> Unit, + onError: (Throwable) -> Unit + ) { + connectionEventRepository.saveEvent(name, method) + SignClient.connect(connect.toSign(), onSuccess) { onError(it.throwable) } + } + + fun authenticate( + name: String, method: String, + authenticate: Modal.Params.Authenticate, + walletAppLink: String? = null, + onSuccess: (String) -> Unit, + onError: (Throwable) -> Unit + ) { + connectionEventRepository.saveEvent(name, method) + SignClient.authenticate(authenticate.toSign(), walletAppLink, onSuccess) { onError(it.throwable) } + } + + fun connectCoinbase( + onSuccess: () -> Unit, + onError: (Throwable) -> Unit + ) { + checkEngineInitialization() + coinbaseClient.connect( + onSuccess = { + scope.launch { + val chain = AppKit.chains.getSelectedChain(AppKit.selectedChain?.id) + saveSessionUseCase(it.toSession(chain)) + AppKitDelegate.emit(it) + onSuccess() + } + + }, onError = { + onError(it) + } + ) + } + + fun getSelectedChain() = getSelectedChainUseCase()?.toChain() + + fun getActiveSession() = getSessionUseCase()?.isSessionActive() + + private fun Session.isSessionActive() = when (this) { + is Session.Coinbase -> if (coinbaseClient.isConnected()) this else null + is Session.WalletConnect -> SignClient.getActiveSessionByTopic(topic)?.let { this } + } + + fun getConnectorType() = getSessionUseCase()?.toConnectorType() + + internal fun getSelectedChainOrFirst() = getSelectedChain() ?: AppKit.chains.first() + + fun formatSIWEMessage(authParams: Modal.Model.AuthPayloadParams, issuer: String): String { + return SignClient.formatAuthMessage(authParams.toSign(issuer)) + } + + fun request(request: Request, onSuccess: (SentRequestResult) -> Unit, onError: (Throwable) -> Unit) { + val session = getActiveSession() + val selectedChain = getSelectedChain() + + if (session == null || selectedChain == null) { + onError(InvalidSessionException) + return + } + + when (session) { + is Session.Coinbase -> { + checkEngineInitialization() + coinbaseClient.request(request, { onSuccess(SentRequestResult.Coinbase(request.method, request.params, selectedChain.id, it)) }, onError) + } + + is Session.WalletConnect -> + SignClient.request(request.toSign(session.topic, selectedChain.id), + { + onSuccess(it.toSentRequest()) + openWalletApp(session.topic, onError) + }, + { onError(it.throwable) } + ) + } + } + + private fun openWalletApp(topic: String, onError: (RedirectMissingThrowable) -> Unit) { + val redirect = SignClient.getActiveSessionByTopic(topic)?.redirect ?: String.Empty + try { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(redirect)) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + wcKoinApp.koin.get().startActivity(intent) + } catch (e: Throwable) { + onError(RedirectMissingThrowable("Please redirect to a wallet manually")) + } + } + + fun ping(sessionPing: Modal.Listeners.SessionPing?) { + when (val session = getSessionUseCase()) { + is Session.WalletConnect -> SignClient.ping(Sign.Params.Ping(session.topic), sessionPing?.toSign()) + else -> sessionPing?.onError(Modal.Model.Ping.Error(InvalidSessionException)) + } + } + + fun disconnect(onSuccess: () -> Unit = {}, onError: (Throwable) -> Unit) { + val session = getSessionUseCase() + + if (session == null) { + onError(InvalidSessionException) + return + } + + when (session) { + is Session.Coinbase -> { + checkEngineInitialization() + coinbaseClient.disconnect() + scope.launch { deleteSessionDataUseCase() } + onSuccess() + } + + is Session.WalletConnect -> { + SignClient.disconnect(Sign.Params.Disconnect(session.topic), + onSuccess = { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.DISCONNECT_SUCCESS)) + scope.launch { deleteSessionDataUseCase() } + shouldDisconnect = true + onSuccess() + }, + onError = { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.DISCONNECT_ERROR)) + onError(it.throwable) + }) + } + } + } + + fun clearSession() { + scope.launch { deleteSessionDataUseCase() } + } + + fun getAccount(): Account? = getActiveSession()?.let { session -> + when (session) { + is Session.Coinbase -> coinbaseClient.getAccount(session) + is Session.WalletConnect -> SignClient.getActiveSessionByTopic(session.topic)?.toAccount(session) + } + } + + fun getSession() = getSessionUseCase()?.let { session -> + when (session) { + is Session.Coinbase -> coinbaseClient.getAccount(session)?.toCoinbaseSession() + is Session.WalletConnect -> SignClient.getActiveSessionByTopic(session.topic)?.toSession() + } + } + + @Throws(IllegalStateException::class) + fun setInternalDelegate(delegate: AppKitDelegate) { + val signDelegate = object : SignClient.DappDelegate { + override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) { + try { + val (name, method) = connectionEventRepository.getEvent() + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_SUCCESS, Properties(name = name, method = method))) + connectionEventRepository.deleteEvent() + } catch (e: Exception) { + logger.error(e) + } + + delegate.onSessionApproved(approvedSession.toModal()) + } + + override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) { + try { + connectionEventRepository.deleteEvent() + } catch (e: Exception) { + logger.error(e) + } + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = rejectedSession.reason))) + delegate.onSessionRejected(rejectedSession.toModal()) + } + + override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) { + delegate.onSessionUpdate(updatedSession.toModal()) + } + + override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) { + delegate.onSessionEvent(sessionEvent.toModal()) + } + + override fun onSessionEvent(sessionEvent: Sign.Model.Event) { + delegate.onSessionEvent(sessionEvent.toModal()) + } + + override fun onSessionExtend(session: Sign.Model.Session) { + delegate.onSessionExtend(session.toModal()) + } + + override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { + clearSession() + delegate.onSessionDelete(deletedSession.toModal()) + } + + override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) { + if (response.result.id == siweRequestIdWithMessage?.first) { + if (response.result is Sign.Model.JsonRpcResponse.JsonRpcResult) { + val siweResponse = Modal.Model.SIWEAuthenticateResponse.Result( + id = response.result.id, + message = siweRequestIdWithMessage!!.second, + signature = (response.result as Sign.Model.JsonRpcResponse.JsonRpcResult).result + ) + siweRequestIdWithMessage = null + delegate.onSIWEAuthenticationResponse(siweResponse) + } else if (response.result is Sign.Model.JsonRpcResponse.JsonRpcError) { + val siweResponse = Modal.Model.SIWEAuthenticateResponse.Error( + id = response.result.id, + message = (response.result as Sign.Model.JsonRpcResponse.JsonRpcError).message, + code = (response.result as Sign.Model.JsonRpcResponse.JsonRpcError).code + ) + siweRequestIdWithMessage = null + delegate.onSIWEAuthenticationResponse(siweResponse) + } + } else { + delegate.onSessionRequestResponse(response.toModal()) + } + } + + override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) { + delegate.onSessionAuthenticateResponse(sessionAuthenticateResponse.toModal()) + } + + override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { + delegate.onProposalExpired(proposal.toModal()) + } + + override fun onRequestExpired(request: Sign.Model.ExpiredRequest) { + delegate.onRequestExpired(request.toModal()) + } + + override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { + delegate.onConnectionStateChange(state.toModal()) + } + + override fun onError(error: Sign.Model.Error) { + delegate.onError(error.toModal()) + } + } + SignClient.setDappDelegate(signDelegate) + } + + fun registerCoinbaseLauncher(activity: ComponentActivity) { + if (::coinbaseClient.isInitialized) { + coinbaseClient.setLauncher(activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> result.data?.data?.let { coinbaseClient.handleResponse(it) } }) + } + } + + fun unregisterCoinbase() { + if (::coinbaseClient.isInitialized) { + coinbaseClient.unregister() + } + } + + fun coinbaseIsEnabled() = ::coinbaseClient.isInitialized && coinbaseClient.isInstalled() && coinbaseClient.isLauncherSet() + + @Throws(IllegalStateException::class) + private fun checkEngineInitialization() { + check(::coinbaseClient.isInitialized) { + "Coinbase Client needs to be initialized first using the initialize function" + } + } + + internal class RedirectMissingThrowable(message: String) : Throwable(message) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseClient.kt b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseClient.kt similarity index 89% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseClient.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseClient.kt index e348ff13c..4edf47fa2 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseClient.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseClient.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.engine.coinbase +package com.reown.appkit.engine.coinbase import android.content.Context import android.content.Intent @@ -19,16 +19,16 @@ import com.coinbase.android.nativesdk.message.request.WALLET_WATCH_ASSET import com.coinbase.android.nativesdk.message.request.Web3JsonRPC import com.coinbase.android.nativesdk.message.response.ActionResult import com.coinbase.android.nativesdk.message.response.ResponseResult -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.models.Account -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.GetSelectedChainUseCase -import com.walletconnect.web3.modal.utils.toChain +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.wcKoinApp +import com.reown.util.Empty +import com.reown.appkit.client.Modal +import com.reown.appkit.client.models.Account +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.GetSelectedChainUseCase +import com.reown.appkit.utils.toChain internal const val COINBASE_WALLET_ID = "fd20dc426fb37566d803205b19bbc1d4096b248ac04548e3cfb6b3a38bd033aa" diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseMapper.kt b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseMapper.kt similarity index 98% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseMapper.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseMapper.kt index 8680e1f02..36322d5aa 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseMapper.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseMapper.kt @@ -1,10 +1,10 @@ -package com.walletconnect.web3.modal.engine.coinbase +package com.reown.appkit.engine.coinbase import com.coinbase.android.nativesdk.message.request.AddChainNativeCurrency import com.coinbase.android.nativesdk.message.request.WatchAssetOptions import com.coinbase.android.nativesdk.message.request.Web3JsonRPC import com.coinbase.android.nativesdk.message.response.ActionResult -import com.walletconnect.util.Empty +import com.reown.util.Empty import org.json.JSONArray import org.json.JSONObject import java.math.BigDecimal diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseResult.kt b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseResult.kt similarity index 76% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseResult.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseResult.kt index 8de6082d0..ba153d7e9 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/coinbase/CoinbaseResult.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/engine/coinbase/CoinbaseResult.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.engine.coinbase +package com.reown.appkit.engine.coinbase sealed class CoinbaseResult { data class Result(val value: String) : CoinbaseResult() diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/presets/AppKitChainsPresets.kt b/product/appkit/src/main/kotlin/com/reown/appkit/presets/AppKitChainsPresets.kt new file mode 100644 index 000000000..c5b137751 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/presets/AppKitChainsPresets.kt @@ -0,0 +1,143 @@ +package com.reown.appkit.presets + +import com.reown.appkit.client.Modal +import com.reown.appkit.utils.EthUtils + +object AppKitChainsPresets { + val ethToken = Modal.Model.Token(name = "Ether", symbol = "ETH", decimal = 18) + + val ethChains: Map = mapOf( + "1" to Modal.Model.Chain( + chainName = "Ethereum", + chainNamespace = "eip155", + chainReference = "1", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://cloudflare-eth.com", + blockExplorerUrl = "https://etherscan.io" + ), + "42161" to Modal.Model.Chain( + chainName = "Arbitrum One", + chainNamespace = "eip155", + chainReference = "42161", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://arb1.arbitrum.io/rpc", + blockExplorerUrl = "https://arbiscan.io" + ), + "137" to Modal.Model.Chain( + chainName = "Polygon", + chainNamespace = "eip155", + chainReference = "137", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = Modal.Model.Token("MATIC", "MATIC", 18), + rpcUrl = "https://polygon-rpc.com", + blockExplorerUrl = "https://polygonscan.com" + ), + "43114" to Modal.Model.Chain( + chainName = "Avalanche", + chainNamespace = "eip155", + chainReference = "43114", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = Modal.Model.Token("Avalanche", "AVAX", 18), + rpcUrl = "https://api.avax.network/ext/bc/C/rpc", + blockExplorerUrl = "https://snowtrace.io" + ), + "56" to Modal.Model.Chain( + chainName = "BNB Smart Chain", + chainNamespace = "eip155", + chainReference = "56", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = Modal.Model.Token("BNB", "BNB", 18), + rpcUrl = "https://rpc.ankr.com/bsc", + blockExplorerUrl = "https://bscscan.com" + ), + "10" to Modal.Model.Chain( + chainName = "OP Mainnet", + chainNamespace = "eip155", + chainReference = "10", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://mainnet.optimism.io", + blockExplorerUrl = "https://explorer.optimism.io" + ), + "100" to Modal.Model.Chain( + chainName = "Gnosis", + chainNamespace = "eip155", + chainReference = "100", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = Modal.Model.Token("Gnosis", "xDAI", 18), + rpcUrl = "https://rpc.gnosischain.com", + blockExplorerUrl = "https://blockscout.com/xdai/mainnet" + ), + "324" to Modal.Model.Chain( + chainName = "zkSync Era", + chainNamespace = "eip155", + chainReference = "324", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://mainnet.era.zksync.io", + blockExplorerUrl = "https://explorer.zksync.io" + ), + "7777777" to Modal.Model.Chain( + chainName = "Zora", + chainNamespace = "eip155", + chainReference = "7777777", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://rpc.zora.energy", + blockExplorerUrl = "https://explorer.zora.energy" + ), + "8453" to Modal.Model.Chain( + chainName = "Base", + chainNamespace = "eip155", + chainReference = "8453", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://mainnet.base.org", + blockExplorerUrl = "https://basescan.org" + ), + "42220" to Modal.Model.Chain( + chainName = "Celo", + chainNamespace = "eip155", + chainReference = "42220", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = Modal.Model.Token("CELO", "CELO", 18), + rpcUrl = "https://forno.celo.org", + blockExplorerUrl = "https://explorer.celo.org/mainnet" + ), + "1313161554" to Modal.Model.Chain( + chainName = "Aurora", + chainNamespace = "eip155", + chainReference = "1313161554", + requiredMethods = EthUtils.ethRequiredMethods, + optionalMethods = EthUtils.ethOptionalMethods, + events = EthUtils.ethEvents, + token = ethToken, + rpcUrl = "https://mainnet.aurora.dev", + blockExplorerUrl = "https://aurorascan.dev" + ), + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKit.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKit.kt new file mode 100644 index 000000000..4313f4163 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKit.kt @@ -0,0 +1,73 @@ +@file:OptIn(ExperimentalMaterialNavigationApi::class) + +package com.reown.appkit.ui + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.NavType +import androidx.navigation.fragment.dialog +import androidx.navigation.navArgument +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.google.accompanist.navigation.material.bottomSheet +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.AppKitComponent +import com.reown.appkit.ui.navigation.Route + +internal const val CHOOSE_NETWORK_KEY = "chooseNetwork" +private const val CHOOSE_NETWORK_ARG = "{chooseNetwork}" +private val appKitPath = Route.APPKIT.path + "/" + CHOOSE_NETWORK_ARG + +fun NavGraphBuilder.appKit() { + dialog(appKitPath) { argument(CHOOSE_NETWORK_KEY) { type = NavType.BoolType } } +} + +@SuppressLint("RestrictedApi") +fun NavController.openAppKit( + shouldOpenChooseNetwork: Boolean = false, + onError: (Throwable) -> Unit = {} +) { + when { + findDestination(R.id.web3ModalGraph) != null -> { + navigate(R.id.web3ModalGraph, args = Bundle().apply { + putBoolean(CHOOSE_NETWORK_KEY, shouldOpenChooseNetwork) + }, navOptions = buildAppKitNavOptions()) + } + findDestination(appKitPath) != null -> { + navigate( + route = Route.APPKIT.path + "/$shouldOpenChooseNetwork", + navOptions = buildAppKitNavOptions() + ) + } + else -> onError(IllegalStateException("Invalid AppKit path")) + } +} + +fun NavGraphBuilder.appKitGraph(navController: NavController) { + bottomSheet( + route = appKitPath, + arguments = listOf(navArgument(CHOOSE_NETWORK_KEY) { type = NavType.BoolType }) + ) { + val shouldOpenChooseNetwork = it.arguments?.getBoolean(CHOOSE_NETWORK_KEY) ?: false + AppKit( + navController = navController, + shouldOpenChooseNetwork = shouldOpenChooseNetwork + ) + } +} + +private fun buildAppKitNavOptions() = NavOptions.Builder().setLaunchSingleTop(true).build() + +@Composable +internal fun AppKit( + navController: NavController, + shouldOpenChooseNetwork: Boolean +) { + AppKitComponent( + closeModal = navController::popBackStack, + shouldOpenChooseNetwork = shouldOpenChooseNetwork + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitEvents.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitEvents.kt new file mode 100644 index 000000000..69b627304 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitEvents.kt @@ -0,0 +1,8 @@ +package com.reown.appkit.ui + +sealed class AppKitEvents { + object SessionApproved: AppKitEvents() + object SessionRejected: AppKitEvents() + object NoAction: AppKitEvents() + object InvalidState: AppKitEvents() +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitSheet.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitSheet.kt new file mode 100644 index 000000000..4576f32dc --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitSheet.kt @@ -0,0 +1,205 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.appkit.ui + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.activity.ComponentDialog +import androidx.activity.OnBackPressedCallback +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.compose.ui.unit.dp +import androidx.core.content.res.getColorOrThrow +import androidx.core.content.res.use +import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.rememberAnimatedNavController +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.reown.modal.utils.theme.toComposeColor +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.AppKitComponent +import com.reown.appkit.ui.theme.ColorPalette + +class AppKitSheet : BottomSheetDialogFragment() { + + override fun onCreate(savedInstanceState: Bundle?) { + requireContext().setTheme(R.style.Web3ModalTheme_DialogTheme) + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val shouldOpenChooseNetwork = arguments.getShouldOpenChooseNetworkArg() + + val mode = requireContext().getThemeMode() + val colors = requireContext().getColorMap() + + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + AppKitTheme( + mode = mode, + lightColors = colors.getLightModeColors(), + darkColors = colors.getDarkModeColors() + ) { + AppKitComposeView(shouldOpenChooseNetwork) + } + } + } + } + + @Composable + private fun AppKitComposeView( + shouldOpenChooseNetwork: Boolean + ) { + val navController = rememberAnimatedNavController() + dialog?.setupDialog(navController) + + Surface(shape = RoundedCornerShape(topStart = 36.dp, topEnd = 36.dp)) { + AppKitComponent( + modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), + navController = navController, + shouldOpenChooseNetwork = shouldOpenChooseNetwork, + closeModal = { this@AppKitSheet.dismiss() }) + } + } + + private fun Dialog.setupDialog(navController: NavHostController) { + (this as? ComponentDialog)?.onBackPressedDispatcher?.addCallback( + this@AppKitSheet, onBackPressedCallback(navController) + ) + findViewById(com.google.android.material.R.id.design_bottom_sheet)?.let { + val behavior = BottomSheetBehavior.from(it) + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } + } + + private fun onBackPressedCallback(navController: NavHostController) = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (navController.popBackStack().not()) { + dismiss() + } + } + } +} + +@Composable +internal fun Map.getLightModeColors(): AppKitTheme.Colors { + val defaultColors = AppKitTheme.provideLightAppKitColors() + val (foreground, background) = provideColorPallets(defaultColors) + return AppKitTheme.provideLightAppKitColors( + accent100 = get(R.attr.modalAccent100) ?: defaultColors.accent100, + accent90 = get(R.attr.modalAccent90) ?: defaultColors.accent90, + accent80 = get(R.attr.modalAccent80) ?: defaultColors.accent80, + foreground = foreground, + background = background, + overlay = get(R.attr.modalGrayGlass) ?: defaultColors.grayGlass, + success = get(R.attr.modalSuccess) ?: defaultColors.success, + error = get(R.attr.modalError) ?: defaultColors.error + ) +} + +@Composable +internal fun Map.getDarkModeColors(): AppKitTheme.Colors { + val defaultColors = AppKitTheme.provideDarkAppKitColor() + val (foreground, background) = provideColorPallets(defaultColors) + return AppKitTheme.provideDarkAppKitColor( + accent100 = get(R.attr.modalAccent100) ?: defaultColors.accent100, + accent90 = get(R.attr.modalAccent90) ?: defaultColors.accent90, + accent80 = get(R.attr.modalAccent80) ?: defaultColors.accent80, + foreground = foreground, + background = background, + overlay = get(R.attr.modalGrayGlass) ?: defaultColors.grayGlass, + success = get(R.attr.modalSuccess) ?: defaultColors.success, + error = get(R.attr.modalError) ?: defaultColors.error + ) +} + +private fun Map.provideColorPallets(defaultColors: AppKitTheme.Colors): Pair { + val foreground = ColorPalette( + color100 = this[R.attr.modalForeground100] ?: defaultColors.foreground.color100, + color125 = this[R.attr.modalForeground125] ?: defaultColors.foreground.color125, + color150 = this[R.attr.modalForeground150] ?: defaultColors.foreground.color150, + color175 = this[R.attr.modalForeground175] ?: defaultColors.foreground.color175, + color200 = this[R.attr.modalForeground200] ?: defaultColors.foreground.color200, + color225 = this[R.attr.modalForeground225] ?: defaultColors.foreground.color225, + color250 = this[R.attr.modalForeground250] ?: defaultColors.foreground.color250, + color275 = this[R.attr.modalForeground275] ?: defaultColors.foreground.color275, + color300 = this[R.attr.modalForeground300] ?: defaultColors.foreground.color300, + ) + val background = ColorPalette( + color100 = this[R.attr.modalBackground100] ?: defaultColors.background.color100, + color125 = this[R.attr.modalBackground125] ?: defaultColors.background.color125, + color150 = this[R.attr.modalBackground150] ?: defaultColors.background.color150, + color175 = this[R.attr.modalBackground175] ?: defaultColors.background.color175, + color200 = this[R.attr.modalBackground200] ?: defaultColors.background.color200, + color225 = this[R.attr.modalBackground225] ?: defaultColors.background.color225, + color250 = this[R.attr.modalBackground250] ?: defaultColors.background.color250, + color275 = this[R.attr.modalBackground275] ?: defaultColors.background.color275, + color300 = this[R.attr.modalBackground300] ?: defaultColors.background.color300, + ) + return (foreground to background) +} + +private fun Int.toThemeMode(): AppKitTheme.Mode = when (this) { + 1 -> AppKitTheme.Mode.DARK + 2 -> AppKitTheme.Mode.LIGHT + else -> AppKitTheme.Mode.AUTO +} + +private val themeColorsAttributesMap = mapOf( + 0 to R.attr.modalAccent100, + 1 to R.attr.modalAccent90, + 2 to R.attr.modalAccent80, + 3 to R.attr.modalForeground100, + 4 to R.attr.modalForeground125, + 5 to R.attr.modalForeground150, + 6 to R.attr.modalForeground175, + 7 to R.attr.modalForeground200, + 8 to R.attr.modalForeground225, + 9 to R.attr.modalForeground250, + 10 to R.attr.modalForeground275, + 11 to R.attr.modalForeground300, + 12 to R.attr.modalBackground100, + 13 to R.attr.modalBackground125, + 14 to R.attr.modalBackground150, + 15 to R.attr.modalBackground175, + 16 to R.attr.modalBackground200, + 17 to R.attr.modalBackground225, + 18 to R.attr.modalBackground250, + 19 to R.attr.modalBackground275, + 20 to R.attr.modalBackground300, + 21 to R.attr.modalGrayGlass, + 22 to R.attr.modalSuccess, + 23 to R.attr.modalError, +) + +internal fun Context.getColorMap() = obtainStyledAttributes(themeColorsAttributesMap.values.toIntArray()).use { + themeColorsAttributesMap.keys.map { id -> + themeColorsAttributesMap[id]!! to try { + it.getColorOrThrow(id).toComposeColor() + } catch (e: Exception) { + null + } + } +}.toMap() + +internal fun Context.getThemeMode() = obtainStyledAttributes(intArrayOf(R.attr.modalMode)).use { it.getInt(0, 0) }.toThemeMode() + +private fun Bundle?.getShouldOpenChooseNetworkArg() = this?.getBoolean(CHOOSE_NETWORK_KEY) ?: false diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitState.kt new file mode 100644 index 000000000..e30c1ee7e --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitState.kt @@ -0,0 +1,11 @@ +package com.reown.appkit.ui + +internal sealed class AppKitState { + object Connect : AppKitState() + + object Loading : AppKitState() + + data class Error(val error: Throwable) : AppKitState() + + object AccountState : AppKitState() +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitTheme.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitTheme.kt new file mode 100644 index 000000000..fe90502ab --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitTheme.kt @@ -0,0 +1,129 @@ +package com.reown.appkit.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.graphics.Color +import com.reown.appkit.ui.theme.ColorPalette +import com.reown.appkit.ui.theme.CustomComposition +import com.reown.appkit.ui.theme.LocalCustomComposition +import com.reown.appkit.ui.theme.defaultDarkAppKitColors +import com.reown.appkit.ui.theme.defaultLightAppKitColors + +@Composable +fun AppKitTheme( + mode: AppKitTheme.Mode = AppKitTheme.Mode.AUTO, + lightColors: AppKitTheme.Colors = AppKitTheme.provideLightAppKitColors(), + darkColors: AppKitTheme.Colors = AppKitTheme.provideDarkAppKitColor(), + content: @Composable () -> Unit +) { + val customComposition = CustomComposition( + mode = mode, + lightColors = lightColors, + darkColors = darkColors, + ) + CompositionLocalProvider( + LocalCustomComposition provides customComposition + ) { + content() + } +} + +object AppKitTheme { + + fun provideLightAppKitColors( + accent100: Color = defaultLightAppKitColors.accent100, + accent90: Color = defaultLightAppKitColors.accent90, + accent80: Color = defaultLightAppKitColors.accent80, + foreground: ColorPalette = defaultLightAppKitColors.foreground, + background: ColorPalette = defaultLightAppKitColors.background, + overlay: Color = defaultLightAppKitColors.grayGlass, + success: Color = defaultLightAppKitColors.success, + error: Color = defaultLightAppKitColors.error + ): Colors = CustomAppKitColor(accent100, accent90, accent80, foreground, background, overlay, success, error) + + fun provideDarkAppKitColor( + accent100: Color = defaultDarkAppKitColors.accent100, + accent90: Color = defaultDarkAppKitColors.accent90, + accent80: Color = defaultDarkAppKitColors.accent80, + foreground: ColorPalette = defaultDarkAppKitColors.foreground, + background: ColorPalette = defaultDarkAppKitColors.background, + overlay: Color = defaultDarkAppKitColors.grayGlass, + success: Color = defaultDarkAppKitColors.success, + error: Color = defaultDarkAppKitColors.error + ): Colors = CustomAppKitColor(accent100, accent90, accent80, foreground, background, overlay, success, error) + + fun provideForegroundLightColorPalette( + color100: Color = defaultLightAppKitColors.foreground.color100, + color125: Color = defaultLightAppKitColors.foreground.color125, + color150: Color = defaultLightAppKitColors.foreground.color150, + color175: Color = defaultLightAppKitColors.foreground.color175, + color200: Color = defaultLightAppKitColors.foreground.color200, + color225: Color = defaultLightAppKitColors.foreground.color225, + color250: Color = defaultLightAppKitColors.foreground.color250, + color275: Color = defaultLightAppKitColors.foreground.color275, + color300: Color = defaultLightAppKitColors.foreground.color300, + ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) + + fun provideForegroundDarkColorPalette( + color100: Color = defaultDarkAppKitColors.foreground.color100, + color125: Color = defaultDarkAppKitColors.foreground.color125, + color150: Color = defaultDarkAppKitColors.foreground.color150, + color175: Color = defaultDarkAppKitColors.foreground.color175, + color200: Color = defaultDarkAppKitColors.foreground.color200, + color225: Color = defaultDarkAppKitColors.foreground.color225, + color250: Color = defaultDarkAppKitColors.foreground.color250, + color275: Color = defaultDarkAppKitColors.foreground.color275, + color300: Color = defaultDarkAppKitColors.foreground.color300, + ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) + + fun provideBackgroundLightColorPalette( + color100: Color = defaultLightAppKitColors.background.color100, + color125: Color = defaultLightAppKitColors.background.color125, + color150: Color = defaultLightAppKitColors.background.color150, + color175: Color = defaultLightAppKitColors.background.color175, + color200: Color = defaultLightAppKitColors.background.color200, + color225: Color = defaultLightAppKitColors.background.color225, + color250: Color = defaultLightAppKitColors.background.color250, + color275: Color = defaultLightAppKitColors.background.color275, + color300: Color = defaultLightAppKitColors.background.color300, + ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) + + fun provideBackgroundDarkColorPalette( + color100: Color = defaultDarkAppKitColors.background.color100, + color125: Color = defaultDarkAppKitColors.background.color125, + color150: Color = defaultDarkAppKitColors.background.color150, + color175: Color = defaultDarkAppKitColors.background.color175, + color200: Color = defaultDarkAppKitColors.background.color200, + color225: Color = defaultDarkAppKitColors.background.color225, + color250: Color = defaultDarkAppKitColors.background.color250, + color275: Color = defaultDarkAppKitColors.background.color275, + color300: Color = defaultDarkAppKitColors.background.color300, + ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) + + + enum class Mode { + LIGHT, DARK, AUTO + } + + interface Colors { + val accent100: Color + val accent90: Color + val accent80: Color + val foreground: ColorPalette + val background: ColorPalette + val grayGlass: Color + val success: Color + val error: Color + } +} + +private class CustomAppKitColor( + override val accent100: Color, + override val accent90: Color, + override val accent80: Color, + override val foreground: ColorPalette, + override val background: ColorPalette, + override val grayGlass: Color, + override val success: Color, + override val error: Color +) : AppKitTheme.Colors diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitView.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitView.kt new file mode 100644 index 000000000..076dac613 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitView.kt @@ -0,0 +1,55 @@ +package com.reown.appkit.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.AppKitComponent + +class AppKitView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + private var shouldOpenNetwork: Boolean + private var closeModal: () -> Unit = {} + + fun setOnCloseModal(onCloseModal: () -> Unit) { + closeModal = onCloseModal + } + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Web3ModalView, 0, 0) + shouldOpenNetwork = typedArray.getBoolean(R.styleable.Web3ModalView_open_network_select, false) + typedArray.recycle() + + context.setTheme(R.style.Web3ModalTheme) + + val mode = context.getThemeMode() + val colors = context.getColorMap() + + LayoutInflater.from(context) + .inflate(R.layout.view_web3modal, this, true) + .findViewById(R.id.root) + .apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + AppKitTheme( + mode = mode, + lightColors = colors.getLightModeColors(), + darkColors = colors.getDarkModeColors() + ) { + AppKitComponent( + shouldOpenChooseNetwork = shouldOpenNetwork, + closeModal = closeModal + ) + } + } + } + } + +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitViewModel.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitViewModel.kt new file mode 100644 index 000000000..a3912a660 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/AppKitViewModel.kt @@ -0,0 +1,49 @@ +package com.reown.appkit.ui + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.reown.android.internal.common.wcKoinApp +import com.reown.appkit.client.AppKit +import com.reown.appkit.engine.AppKitEngine +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +internal class AppKitViewModel : ViewModel() { + + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() + private val _modalState: MutableStateFlow = MutableStateFlow(AppKitState.Loading) + val shouldDisconnect get() = appKitEngine.shouldDisconnect + + val modalState: StateFlow + get() = _modalState.asStateFlow() + + init { + require(AppKit.chains.isNotEmpty()) { "Be sure to set the Chains using AppKit.setChains" } + initModalState() + } + + fun disconnect() { + appKitEngine.disconnect( + onSuccess = { println("Disconnected successfully") }, + onError = { println("Disconnect error: $it") } + ) + } + + internal fun initModalState() { + viewModelScope.launch { + appKitEngine.getActiveSession()?.let { _ -> + createAccountModalState() + } ?: createConnectModalState() + } + } + + private fun createAccountModalState() { + _modalState.value = AppKitState.AccountState + } + + private fun createConnectModalState() { + _modalState.value = AppKitState.Connect + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/ComponentDelegate.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/ComponentDelegate.kt similarity index 86% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/ComponentDelegate.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/ComponentDelegate.kt index 84afaebb3..fe30971fa 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/ComponentDelegate.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/ComponentDelegate.kt @@ -1,6 +1,6 @@ -package com.walletconnect.web3.modal.ui.components +package com.reown.appkit.ui.components -import com.walletconnect.web3.modal.client.Web3Modal +import com.reown.appkit.client.AppKit import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.onEach @@ -15,7 +15,7 @@ internal object ComponentDelegate { var isModalOpen: Boolean = false - fun setDelegate(delegate: Web3Modal.ComponentDelegate) { + fun setDelegate(delegate: AppKit.ComponentDelegate) { modalComponentEvent.onEach { event -> when (event) { ComponentEvent.ModalHiddenEvent -> delegate.onModalHidden() diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AccountButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AccountButton.kt new file mode 100644 index 000000000..2bb56a04c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AccountButton.kt @@ -0,0 +1,215 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.client.Modal +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.TransparentSurface +import com.reown.appkit.ui.components.internal.commons.account.generateAvatarColors +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.components.internal.commons.button.TextButton +import com.reown.appkit.ui.components.internal.commons.network.CircleNetworkImage +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.getImageData +import com.reown.appkit.utils.toVisibleAddress + +enum class AccountButtonType { + NORMAL, MIXED +} + +internal sealed class AccountButtonState { + object Loading : AccountButtonState() + + data class Normal(val address: String) : AccountButtonState() + + data class Mixed( + val address: String, + val chainImage: Modal.Model.ChainImage, + val chainName: String, + val balance: String? + ) : AccountButtonState() + + object Invalid : AccountButtonState() +} + +@Composable +fun AccountButton( + state: AppKitState, + accountButtonType: AccountButtonType = AccountButtonType.NORMAL +) { + val accountState by when (accountButtonType) { + AccountButtonType.NORMAL -> state.accountNormalButtonState.collectAsState() + AccountButtonType.MIXED -> state.accountMixedButtonState.collectAsState() + } + AccountButtonState( + state = accountState, + onClick = state::openAppKit + ) +} + +@Composable +internal fun AccountButtonState( + state: AccountButtonState, + onClick: () -> Unit, +) { + when (state) { + AccountButtonState.Invalid -> UnavailableSession() + AccountButtonState.Loading -> LoadingButton() + is AccountButtonState.Normal -> AccountButtonNormal(address = state.address, onClick = onClick) + is AccountButtonState.Mixed -> AccountButtonMixed( + address = state.address, + chainImage = state.chainImage, + chainData = state.balance ?: state.chainName, + onClick = onClick + ) + } +} + +@Composable +private fun AccountButtonMixed( + address: String, + chainImage: Modal.Model.ChainImage, + chainData: String, + onClick: () -> Unit, + isEnabled: Boolean = true +) { + ProvideAppKitThemeComposition { + val backgroundColor: Color + val borderColor: Color + val textColor: Color + + if (isEnabled) { + backgroundColor = AppKitTheme.colors.grayGlass02 + borderColor = AppKitTheme.colors.grayGlass05 + textColor = AppKitTheme.colors.foreground.color100 + } else { + backgroundColor = AppKitTheme.colors.grayGlass15 + borderColor = AppKitTheme.colors.grayGlass05 + textColor = AppKitTheme.colors.grayGlass15 + } + + TransparentSurface(shape = RoundedCornerShape(100)) { + Box( + modifier = Modifier + .clickable(isEnabled) { onClick() } + .border(width = 1.dp, color = borderColor, shape = CircleShape) + .height(40.dp) + .background(backgroundColor) + ) { + Row( + modifier = Modifier.padding(start = 8.dp, end = 4.dp).fillMaxHeight(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + CircleNetworkImage(data = chainImage.getImageData(), size = 24.dp, isEnabled = isEnabled) + HorizontalSpacer(width = 4.dp) + Text(text = chainData, style = AppKitTheme.typo.paragraph600.copy(color = textColor)) + HorizontalSpacer(width = 8.dp) + ImageButton( + text = address.toVisibleAddress(), image = { + Box( + modifier = Modifier + .size(22.dp) + .border(width = 2.dp, color = AppKitTheme.colors.grayGlass05, shape = CircleShape) + .padding(2.dp) + .background(brush = Brush.linearGradient(generateAvatarColors(address)), shape = CircleShape) + ) + }, + paddingValues = PaddingValues(start = 4.dp, end = 8.dp), + isEnabled = isEnabled, + style = ButtonStyle.ACCOUNT, + size = ButtonSize.ACCOUNT_S, + onClick = onClick + ) + } + } + } + } +} + +@Composable +private fun AccountButtonNormal( + address: String, + onClick: () -> Unit, + isEnabled: Boolean = true +) { + ProvideAppKitThemeComposition { + ImageButton( + text = address.toVisibleAddress(), image = { + Box( + modifier = Modifier + .size(22.dp) + .border(width = 2.dp, color = AppKitTheme.colors.grayGlass05, shape = CircleShape) + .padding(2.dp) + .background(brush = Brush.linearGradient(generateAvatarColors(address)), shape = CircleShape) + ) + }, + paddingValues = PaddingValues(start = 6.dp, end = 12.dp, top = 4.dp, bottom = 4.dp), + isEnabled = isEnabled, + style = ButtonStyle.ACCOUNT, + size = ButtonSize.ACCOUNT_S, + onClick = onClick + ) + } +} + + +@Composable +private fun UnavailableSession() { + ProvideAppKitThemeComposition { + TextButton(text = "Session Unavailable", style = ButtonStyle.ACCOUNT, size = ButtonSize.M, isEnabled = false, onClick = {}) + } +} + +@UiModePreview +@Composable +private fun UnavailableSessionPreview() { + ComponentPreview { + UnavailableSession() + } +} + +@UiModePreview +@Composable +private fun AccountButtonNormalPreview() { + MultipleComponentsPreview( + { AccountButtonNormal(address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}) }, + { AccountButtonNormal(address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}, isEnabled = false) } + ) +} + +@UiModePreview +@Composable +private fun AccountButtonMixedPreview() { + MultipleComponentsPreview( + { AccountButtonMixed(chainData = "ETH", chainImage = Modal.Model.ChainImage.Asset(R.drawable.ic_select_network), address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}) }, + { AccountButtonMixed(chainData = "ETH", chainImage = Modal.Model.ChainImage.Asset(R.drawable.ic_select_network), address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}, isEnabled = false) } + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AppKitState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AppKitState.kt new file mode 100644 index 000000000..5f63fd031 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/AppKitState.kt @@ -0,0 +1,126 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.navigation.NavController +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.GetEthBalanceUseCase +import com.reown.appkit.domain.usecase.GetSessionUseCase +import com.reown.appkit.domain.usecase.ObserveSelectedChainUseCase +import com.reown.appkit.domain.usecase.ObserveSessionUseCase +import com.reown.appkit.engine.AppKitEngine +import com.reown.appkit.ui.components.ComponentDelegate +import com.reown.appkit.ui.components.ComponentEvent +import com.reown.appkit.ui.openAppKit +import com.reown.appkit.utils.getChainNetworkImageUrl +import com.reown.appkit.utils.getChains +import com.reown.appkit.utils.getSelectedChain +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@Composable +fun rememberAppKitState( + coroutineScope: CoroutineScope = rememberCoroutineScope(), + navController: NavController +): AppKitState { + return remember(navController) { + AppKitState(coroutineScope, navController) + } +} + +class AppKitState( + coroutineScope: CoroutineScope, + private val navController: NavController +) { + private val logger: Logger = wcKoinApp.koin.get() + private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() + private val observeSessionTopicUseCase: ObserveSessionUseCase = wcKoinApp.koin.get() + private val getSessionUseCase: GetSessionUseCase = wcKoinApp.koin.get() + private val getEthBalanceUseCase: GetEthBalanceUseCase = wcKoinApp.koin.get() + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() + private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() + private val sessionTopicFlow = observeSessionTopicUseCase() + + val isOpen = ComponentDelegate.modalComponentEvent + .map { event -> + sendModalCloseOrOpenEvents(event) + event.isOpen + } + .stateIn(coroutineScope, started = SharingStarted.Lazily, ComponentDelegate.isModalOpen) + + val isConnected = sessionTopicFlow + .map { it != null && getSessionUseCase() != null } + .map { AppKit.getAccount() != null } + .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = false) + + internal val selectedChain = observeSelectedChainUseCase().map { savedChainId -> + AppKit.chains.find { it.id == savedChainId } + } + + internal val accountNormalButtonState = sessionTopicFlow.combine(selectedChain) { session, chain -> session to chain } + .mapOrAccountState(AccountButtonType.NORMAL) + .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = AccountButtonState.Loading) + + internal val accountMixedButtonState = sessionTopicFlow.combine(selectedChain) { session, chain -> session to chain } + .mapOrAccountState(AccountButtonType.MIXED) + .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = AccountButtonState.Loading) + + private fun Flow>.mapOrAccountState(accountButtonType: AccountButtonType) = + map { appKitEngine.getActiveSession()?.mapToAccountButtonState(accountButtonType) ?: AccountButtonState.Invalid } + + private fun sendModalCloseOrOpenEvents(event: ComponentEvent) { + when { + event.isOpen && isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_OPEN, Properties(connected = true))) + event.isOpen && !isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_OPEN, Properties(connected = false))) + !event.isOpen && isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_CLOSE, Properties(connected = true))) + !event.isOpen && !isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_CLOSE, Properties(connected = false))) + } + } + + private suspend fun Session.mapToAccountButtonState(accountButtonType: AccountButtonType) = try { + val chains = getChains() + val selectedChain = chains.getSelectedChain(this.chain) + val address = this.address + when (accountButtonType) { + AccountButtonType.NORMAL -> AccountButtonState.Normal(address = address) + AccountButtonType.MIXED -> { + val balance = getBalance(selectedChain, address) + AccountButtonState.Mixed( + address = address, + chainImage = selectedChain.chainImage ?: getChainNetworkImageUrl(selectedChain.chainReference), + chainName = selectedChain.chainName, + balance = balance + ) + } + } + } catch (e: Exception) { + AccountButtonState.Invalid + } + + private suspend fun getBalance(selectedChain: Modal.Model.Chain, address: String) = + selectedChain.rpcUrl?.let { url -> getEthBalanceUseCase(selectedChain.token, url, address)?.valueWithSymbol } + + internal fun openAppKit(shouldOpenChooseNetwork: Boolean = false, isActiveNetwork: Boolean = false) { + if (shouldOpenChooseNetwork && isActiveNetwork) { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORKS)) + } + + navController.openAppKit( + shouldOpenChooseNetwork = shouldOpenChooseNetwork, + onError = { logger.error(it) } + ) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/ConnectButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/ConnectButton.kt new file mode 100644 index 000000000..5aaa063b9 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/ConnectButton.kt @@ -0,0 +1,97 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.components.internal.commons.button.TextButton +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition + +enum class ConnectButtonSize { + NORMAL, SMALL +} + +@Composable +fun ConnectButton( + state: AppKitState, + buttonSize: ConnectButtonSize = ConnectButtonSize.NORMAL +) { + val isLoading: Boolean by state.isOpen.collectAsState(initial = false) + val isConnected: Boolean by state.isConnected.collectAsState(initial = false) + + ConnectButton( + size = buttonSize, + isLoading = isLoading, + isEnabled = !isConnected + ) { + state.openAppKit() + } +} + +@Composable +internal fun ConnectButton( + size: ConnectButtonSize, + isLoading: Boolean = false, + isEnabled: Boolean = true, + onClick: () -> Unit, +) { + ProvideAppKitThemeComposition { + val buttonSize = when (size) { + ConnectButtonSize.NORMAL -> ButtonSize.M + ConnectButtonSize.SMALL -> ButtonSize.S + } + if (isLoading && isEnabled) { + ImageButton( + text = "Connecting...", + image = { LoadingSpinner(size = 10.dp, strokeWidth = 2.dp) }, + style = ButtonStyle.LOADING, + size = buttonSize + ) {} + } else { + val text = when (size) { + ConnectButtonSize.NORMAL -> "Connect wallet" + ConnectButtonSize.SMALL -> "Connect" + } + TextButton( + text = text, + style = ButtonStyle.MAIN, + size = buttonSize, + isEnabled = isEnabled, + onClick = onClick + ) + } + } +} + +@UiModePreview +@Composable +private fun ConnectButtonPreview() { + MultipleComponentsPreview( + { ConnectButton(size = ConnectButtonSize.NORMAL) {} }, + { ConnectButton(size = ConnectButtonSize.SMALL) {} }, + ) +} + +@UiModePreview +@Composable +private fun DisabledConnectButtonPreview() { + MultipleComponentsPreview( + { ConnectButton(size = ConnectButtonSize.NORMAL, isEnabled = false) {} }, + { ConnectButton(size = ConnectButtonSize.SMALL, isEnabled = false) {} }, + ) +} + +@UiModePreview +@Composable +private fun LoadingConnectButtonPreview() { + MultipleComponentsPreview( + { ConnectButton(size = ConnectButtonSize.NORMAL, isLoading = true) {} }, + { ConnectButton(size = ConnectButtonSize.SMALL, isLoading = true) {} }, + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/LoadingButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/LoadingButton.kt new file mode 100644 index 000000000..f7b0ba03b --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/LoadingButton.kt @@ -0,0 +1,36 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.StyledButton +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition + +@Composable +internal fun LoadingButton() { + ProvideAppKitThemeComposition { + StyledButton(style = ButtonStyle.ACCOUNT, size = ButtonSize.M, onClick = {}) { + Box( + modifier = Modifier.width(100.dp), contentAlignment = Alignment.Center + ) { + LoadingSpinner(size = 16.dp, strokeWidth = 1.dp) + } + } + } +} + +@UiModePreview +@Composable +private fun LoadingButtonPreview() { + ComponentPreview { + LoadingButton() + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/NetworkButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/NetworkButton.kt new file mode 100644 index 000000000..3c97eef9d --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/NetworkButton.kt @@ -0,0 +1,50 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.SelectNetworkIcon +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.components.internal.commons.network.CircleNetworkImage +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import com.reown.appkit.utils.getImageData + +@Composable +fun NetworkButton( + state: AppKitState +) { + val selectedChain by state.selectedChain.collectAsState(initial = null) + val image: @Composable () -> Unit = selectedChain?.let { chain -> + { CircleNetworkImage(data = chain.getImageData(), size = 24.dp) } + } ?: { + SelectNetworkIcon() + } + NetworkButton( + text = selectedChain?.chainName ?: "Select Network", + image = image, + isEnabled = true, + onClick = { state.openAppKit(true, selectedChain != null) } + ) +} + +@Composable +internal fun NetworkButton( + text: String, + image: @Composable () -> Unit, + onClick: () -> Unit, + isEnabled: Boolean = true +) { + ProvideAppKitThemeComposition { + ImageButton( + text = text, + image = { image() }, + isEnabled = isEnabled, + style = ButtonStyle.ACCOUNT, + size = ButtonSize.ACCOUNT_M, + onClick = onClick + ) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/Web3Button.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/Web3Button.kt new file mode 100644 index 000000000..1e92a3b9f --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/Web3Button.kt @@ -0,0 +1,26 @@ +package com.reown.appkit.ui.components.button + +import androidx.compose.animation.AnimatedContent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue + +@Composable +fun Web3Button( + state: AppKitState, + accountButtonType: AccountButtonType = AccountButtonType.NORMAL, + connectButtonSize: ConnectButtonSize = ConnectButtonSize.NORMAL +) { + val isConnected by state.isConnected.collectAsState(initial = false) + + AnimatedContent( + targetState = isConnected, + label = "Web3ButtonState" + ) { isConnected -> + if (isConnected) { + AccountButton(state = state, accountButtonType = accountButtonType) + } else { + ConnectButton(state = state, buttonSize = connectButtonSize) + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/AccountButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/AccountButton.kt new file mode 100644 index 000000000..73db9c3dc --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/AccountButton.kt @@ -0,0 +1,38 @@ +package com.reown.appkit.ui.components.button.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.navigation.findNavController +import com.reown.appkit.R +import com.reown.appkit.ui.components.button.AccountButton +import com.reown.appkit.ui.components.button.rememberAppKitState +import com.reown.appkit.utils.toAccountButtonType + +class AccountButton @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AccountButton, 0, 0) + val accountButtonType = typedArray.getInteger(R.styleable.AccountButton_account_button_type, 0).toAccountButtonType() + typedArray.recycle() + + LayoutInflater.from(context) + .inflate(R.layout.view_button, this, true) + .findViewById(R.id.root) + .apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + val appKitState = rememberAppKitState(navController = findNavController()) + AccountButton( + state = appKitState, + accountButtonType = accountButtonType + ) + } + } + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/ConnectButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/ConnectButton.kt new file mode 100644 index 000000000..25fcbd151 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/ConnectButton.kt @@ -0,0 +1,35 @@ +package com.reown.appkit.ui.components.button.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.navigation.findNavController +import com.reown.appkit.R +import com.reown.appkit.ui.components.button.ConnectButton +import com.reown.appkit.ui.components.button.rememberAppKitState +import com.reown.appkit.utils.toConnectButtonSize + +class ConnectButton @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ConnectButton, 0, 0) + val connectButtonSize = typedArray.getInteger(R.styleable.ConnectButton_connect_button_size, 0).toConnectButtonSize() + typedArray.recycle() + + LayoutInflater.from(context) + .inflate(R.layout.view_button, this, true) + .findViewById(R.id.root) + .apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + val appKitState = rememberAppKitState(navController = findNavController()) + ConnectButton(state = appKitState, buttonSize = connectButtonSize) + } + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/Web3Button.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/Web3Button.kt new file mode 100644 index 000000000..89de4170e --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/button/views/Web3Button.kt @@ -0,0 +1,41 @@ +package com.reown.appkit.ui.components.button.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.navigation.findNavController +import com.reown.appkit.R +import com.reown.appkit.ui.components.button.Web3Button +import com.reown.appkit.ui.components.button.rememberAppKitState +import com.reown.appkit.utils.toAccountButtonType +import com.reown.appkit.utils.toConnectButtonSize + +class Web3Button @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Web3Button, 0, 0) + val connectButtonSize = typedArray.getInteger(R.styleable.Web3Button_connect_button_size, 0).toConnectButtonSize() + val accountButtonType = typedArray.getInteger(R.styleable.Web3Button_account_button_type, 0).toAccountButtonType() + typedArray.recycle() + + LayoutInflater.from(context) + .inflate(R.layout.view_button, this, true) + .findViewById(R.id.root) + .apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + val appKitState = rememberAppKitState(navController = findNavController()) + Web3Button( + state = appKitState, + accountButtonType = accountButtonType, + connectButtonSize = connectButtonSize + ) + } + } + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitComponent.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitComponent.kt new file mode 100644 index 000000000..89025fcfe --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitComponent.kt @@ -0,0 +1,127 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.appkit.ui.components.internal + +import android.annotation.SuppressLint +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.togetherWith +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.rememberAnimatedNavController +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.ui.AppKitState +import com.reown.appkit.ui.AppKitViewModel +import com.reown.appkit.ui.components.internal.root.AppKitRoot +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.account.AccountNavGraph +import com.reown.appkit.ui.routes.connect.ConnectionNavGraph +import com.reown.appkit.ui.utils.ComposableLifecycleEffect +import com.reown.appkit.ui.utils.toComponentEvent +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +@Composable +fun AppKitComponent( + shouldOpenChooseNetwork: Boolean, + closeModal: () -> Unit +) { + AppKitComponent( + navController = rememberAnimatedNavController(), + shouldOpenChooseNetwork = shouldOpenChooseNetwork, + closeModal = closeModal + ) +} + +@SuppressLint("RestrictedApi") +@Composable +internal fun AppKitComponent( + modifier: Modifier = Modifier, + navController: NavHostController = rememberAnimatedNavController(), + shouldOpenChooseNetwork: Boolean, + closeModal: () -> Unit +) { + val appKitViewModel: AppKitViewModel = viewModel() + val state by appKitViewModel.modalState.collectAsState() + val coroutineScope = rememberCoroutineScope() + + LaunchedEffect(Unit) { + AppKitDelegate + .wcEventModels + .onEach { event -> + when (event) { + is Modal.Model.SIWEAuthenticateResponse.Result, is Modal.Model.SessionAuthenticateResponse.Result -> closeModal() + is Modal.Model.ApprovedSession -> { + if (AppKit.authPayloadParams != null) { + navController.navigate(Route.SIWE_FALLBACK.path) + } else { + closeModal() + } + } + is Modal.Model.DeletedSession.Success -> closeModal() + + else -> Unit + } + } + .collect() + } + + ComposableLifecycleEffect( + onEvent = { _, event -> + coroutineScope.launch { + event.toComponentEvent(onClosed = { + if (navController.currentDestination?.route == Route.SIWE_FALLBACK.path && appKitViewModel.shouldDisconnect) { + appKitViewModel.disconnect() + } + }) + } + } + ) + + AppKitRoot( + modifier = modifier, + navController = navController, + closeModal = closeModal + ) { + AnimatedContent( + targetState = state, + contentAlignment = Alignment.BottomCenter, + transitionSpec = { + (fadeIn() + slideInVertically(animationSpec = tween(500), + initialOffsetY = { fullHeight -> fullHeight })).togetherWith(fadeOut(animationSpec = tween(500))) + }, + label = "Root Animated content" + ) { state -> + when (state) { + is AppKitState.Connect -> ConnectionNavGraph( + navController = navController, + closeModal = closeModal, + shouldOpenChooseNetwork = shouldOpenChooseNetwork + ) + + is AppKitState.AccountState -> AccountNavGraph( + navController = navController, + closeModal = closeModal, + shouldOpenChangeNetwork = shouldOpenChooseNetwork + ) + + AppKitState.Loading -> LoadingModalState() + is AppKitState.Error -> ErrorModalState(retry = appKitViewModel::initModalState) + } + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitStates.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitStates.kt new file mode 100644 index 000000000..2da30ac9b --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitStates.kt @@ -0,0 +1,50 @@ +package com.reown.appkit.ui.components.internal + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.reown.modal.ui.components.common.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.TryAgainButton +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun LoadingModalState() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(300.dp), + contentAlignment = Alignment.Center, + ) { + LoadingSpinner() + } +} + +@Composable +internal fun ErrorModalState(retry: () -> Unit) { + Column( + modifier = Modifier + .fillMaxWidth() + .height(300.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = "Something went wrong", style = AppKitTheme.typo.paragraph400) + VerticalSpacer(height = 10.dp) + TryAgainButton( + size = ButtonSize.M, + style = ButtonStyle.MAIN + ) { + retry() + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBar.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBar.kt new file mode 100644 index 000000000..6bf18e584 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/AppKitTopBar.kt @@ -0,0 +1,54 @@ +package com.reown.appkit.ui.components.internal + +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.ui.components.internal.commons.BackArrowIcon +import com.reown.appkit.ui.components.internal.commons.CloseIcon +import com.reown.appkit.ui.components.internal.commons.QuestionMarkIcon +import com.reown.appkit.ui.components.internal.commons.TestTags +import com.reown.appkit.ui.previews.MultipleComponentsPreview + +@Composable +internal fun AppKitTopBar( + title: String, + startIcon: @Composable () -> Unit, + onCloseIconClick: () -> Unit +) { + Row( + modifier = Modifier + .height(64.dp) + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier.size(40.dp)) { + startIcon() + } + Text( + text = title, + style = AppKitTheme.typo.paragraph600.copy( + color = AppKitTheme.colors.foreground.color100, + textAlign = TextAlign.Center + ), + modifier = Modifier.weight(1f).testTag(TestTags.TITLE) + ) + CloseIcon(onClick = onCloseIconClick) + } +} + +@Composable +@UiModePreview +private fun PreviewAppKitTopBar() { + MultipleComponentsPreview( + { AppKitTopBar(title = "WalletConnect", startIcon = { BackArrowIcon {} }, {}) }, + { AppKitTopBar(title = "WalletConnect", startIcon = { QuestionMarkIcon() }, {}) } + ) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Orientation.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/Orientation.kt similarity index 94% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Orientation.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/Orientation.kt index 5495b6f89..193af8723 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Orientation.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/Orientation.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal +package com.reown.appkit.ui.components.internal import android.content.res.Configuration import androidx.compose.runtime.Composable diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ContentDescription.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ContentDescription.kt similarity index 90% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ContentDescription.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ContentDescription.kt index c89e9a38e..2ba17b674 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ContentDescription.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ContentDescription.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons enum class ContentDescription(val description: String) { BACK_ARROW("BackArrow"), diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Dividers.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Dividers.kt new file mode 100644 index 000000000..8ccd0d153 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Dividers.kt @@ -0,0 +1,11 @@ +package com.reown.appkit.ui.components.internal.commons + +import androidx.compose.material.Divider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun FullWidthDivider(modifier: Modifier = Modifier) { + Divider(color = AppKitTheme.colors.grayGlass05, modifier = modifier) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Help.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Help.kt similarity index 81% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Help.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Help.kt index 92aa02525..bb9e6a740 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Help.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Help.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons import androidx.compose.foundation.Image import androidx.compose.foundation.clickable @@ -19,10 +19,10 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.R +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun HelpSection( @@ -48,13 +48,13 @@ internal fun HelpSection( Spacer(modifier = Modifier.height(20.dp)) Text( text = title, - style = Web3ModalTheme.typo.paragraph400, + style = AppKitTheme.typo.paragraph400, textAlign = TextAlign.Center, ) Spacer(modifier = Modifier.height(8.dp)) Text( text = body, - style = Web3ModalTheme.typo.small400.copy(Web3ModalTheme.colors.foreground.color200), + style = AppKitTheme.typo.small400.copy(AppKitTheme.colors.foreground.color200), textAlign = TextAlign.Center, ) } @@ -68,7 +68,7 @@ internal fun NetworkBottomSection( VerticalSpacer(height = 12.dp) Text( text = "Your connected wallet may not support some of the networks available for this dApp", - style = Web3ModalTheme.typo.small500.copy(color = Web3ModalTheme.colors.foreground.color300), + style = AppKitTheme.typo.small500.copy(color = AppKitTheme.colors.foreground.color300), textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = 12.dp) ) @@ -87,11 +87,11 @@ internal fun WhatIsNetworkLink(onClick: () -> Unit) { horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { - QuestionMarkIcon(tint = Web3ModalTheme.colors.accent100, modifier = Modifier.size(12.dp)) + QuestionMarkIcon(tint = AppKitTheme.colors.accent100, modifier = Modifier.size(12.dp)) HorizontalSpacer(width = 4.dp) Text( text = "What is a network", - style = Web3ModalTheme.typo.small600.copy(color = Web3ModalTheme.colors.accent100) + style = AppKitTheme.typo.small600.copy(color = AppKitTheme.colors.accent100) ) } } diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Icons.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Icons.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Icons.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Icons.kt index 7f1d9e662..14fa77dda 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Icons.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Icons.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -14,15 +14,15 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.walletconnect.modal.ui.components.common.roundedClickable -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.modal.ui.components.common.roundedClickable +import com.reown.appkit.R +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun BackArrowIcon( - tint: Color = Web3ModalTheme.colors.foreground.color100, + tint: Color = AppKitTheme.colors.foreground.color100, onClick: () -> Unit ) { Icon( @@ -39,7 +39,7 @@ internal fun BackArrowIcon( @Composable internal fun QuestionMarkIcon( modifier: Modifier = Modifier, - tint: Color = Web3ModalTheme.colors.foreground.color100 + tint: Color = AppKitTheme.colors.foreground.color100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_question_mark), @@ -52,7 +52,7 @@ internal fun QuestionMarkIcon( @Composable internal fun CloseIcon( modifier: Modifier = Modifier, - tint: Color = Web3ModalTheme.colors.foreground.color100, + tint: Color = AppKitTheme.colors.foreground.color100, onClick: () -> Unit ) { Icon( @@ -68,7 +68,7 @@ internal fun CloseIcon( @Composable internal fun RetryIcon( - tint: Color = Web3ModalTheme.colors.inverse100 + tint: Color = AppKitTheme.colors.inverse100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_retry), @@ -82,18 +82,18 @@ internal fun RetryIcon( internal fun DeclinedIcon() { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_close), - tint = Web3ModalTheme.colors.error, + tint = AppKitTheme.colors.error, contentDescription = ContentDescription.DECLINED.description, modifier = Modifier .size(20.dp) - .background(Web3ModalTheme.colors.error.copy(alpha = .2f), shape = CircleShape) + .background(AppKitTheme.colors.error.copy(alpha = .2f), shape = CircleShape) .padding(4.dp) ) } @Composable internal fun WalletIcon( - tint: Color = Web3ModalTheme.colors.inverse100 + tint: Color = AppKitTheme.colors.inverse100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet), @@ -105,7 +105,7 @@ internal fun WalletIcon( @Composable internal fun ExternalIcon( - tint: Color = Web3ModalTheme.colors.foreground.color200, + tint: Color = AppKitTheme.colors.foreground.color200, size: Dp = 12.dp, ) { Icon( @@ -119,7 +119,7 @@ internal fun ExternalIcon( @Composable internal fun CopyIcon( modifier: Modifier = Modifier, - tint: Color = Web3ModalTheme.colors.foreground.color250 + tint: Color = AppKitTheme.colors.foreground.color250 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_copy), @@ -131,7 +131,7 @@ internal fun CopyIcon( @Composable internal fun CompassIcon( - tint: Color = Web3ModalTheme.colors.foreground.color150 + tint: Color = AppKitTheme.colors.foreground.color150 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_compass), @@ -143,7 +143,7 @@ internal fun CompassIcon( @Composable internal fun ChevronRightIcon( - tint: Color = Web3ModalTheme.colors.foreground.color200 + tint: Color = AppKitTheme.colors.foreground.color200 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_chevron_right), @@ -155,16 +155,16 @@ internal fun ChevronRightIcon( @Composable internal fun RecentTransactionIcon( - tint: Color = Web3ModalTheme.colors.accent100 + tint: Color = AppKitTheme.colors.accent100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_swap), contentDescription = ContentDescription.SWAP.description, modifier = Modifier - .border(2.dp, Web3ModalTheme.colors.grayGlass02, shape = CircleShape) + .border(2.dp, AppKitTheme.colors.grayGlass02, shape = CircleShape) .padding(2.dp) .size(32.dp) - .background(Web3ModalTheme.colors.grayGlass10, shape = CircleShape) + .background(AppKitTheme.colors.grayGlass10, shape = CircleShape) .padding(8.dp), tint = tint ) @@ -172,16 +172,16 @@ internal fun RecentTransactionIcon( @Composable internal fun DisconnectIcon( - tint: Color = Web3ModalTheme.colors.foreground.color200 + tint: Color = AppKitTheme.colors.foreground.color200 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_disconnect), contentDescription = ContentDescription.DISCONNECT.description, modifier = Modifier - .border(2.dp, Web3ModalTheme.colors.grayGlass05, shape = CircleShape) + .border(2.dp, AppKitTheme.colors.grayGlass05, shape = CircleShape) .padding(2.dp) .size(32.dp) - .background(Web3ModalTheme.colors.grayGlass10, shape = CircleShape) + .background(AppKitTheme.colors.grayGlass10, shape = CircleShape) .padding(8.dp), tint = tint ) @@ -189,7 +189,7 @@ internal fun DisconnectIcon( @Composable internal fun ScanQRIcon( - tint: Color = Web3ModalTheme.colors.accent100, + tint: Color = AppKitTheme.colors.accent100, onClick: () -> Unit ) { Icon( @@ -199,12 +199,12 @@ internal fun ScanQRIcon( .roundedClickable { onClick() } .size(40.dp) .background( - color = Web3ModalTheme.colors.accent10, + color = AppKitTheme.colors.accent10, shape = RoundedCornerShape(12.dp) ) .border( width = 1.dp, - color = Web3ModalTheme.colors.accent10, + color = AppKitTheme.colors.accent10, shape = RoundedCornerShape(12.dp) ) .padding(10.dp), @@ -214,7 +214,7 @@ internal fun ScanQRIcon( @Composable internal fun AllWalletsIcon( - tint: Color = Web3ModalTheme.colors.accent100, + tint: Color = AppKitTheme.colors.accent100, ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_all_wallets), @@ -222,12 +222,12 @@ internal fun AllWalletsIcon( modifier = Modifier .size(40.dp) .background( - color = Web3ModalTheme.colors.accent10, + color = AppKitTheme.colors.accent10, shape = RoundedCornerShape(12.dp) ) .border( width = 1.dp, - color = Web3ModalTheme.colors.accent10, + color = AppKitTheme.colors.accent10, shape = RoundedCornerShape(12.dp) ) .padding(8.dp), @@ -237,7 +237,7 @@ internal fun AllWalletsIcon( @Composable internal fun SelectNetworkIcon( - tint: Color = Web3ModalTheme.colors.foreground.color100 + tint: Color = AppKitTheme.colors.foreground.color100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_select_network), @@ -245,13 +245,13 @@ internal fun SelectNetworkIcon( modifier = Modifier .size(24.dp) .background( - color = Web3ModalTheme.colors.grayGlass25, + color = AppKitTheme.colors.grayGlass25, shape = CircleShape ) .padding(5.dp) .border( width = 1.dp, - color = Web3ModalTheme.colors.grayGlass10, + color = AppKitTheme.colors.grayGlass10, shape = CircleShape ), tint = tint @@ -260,7 +260,7 @@ internal fun SelectNetworkIcon( @Composable internal fun MobileIcon( - tint: Color = Web3ModalTheme.colors.inverse100 + tint: Color = AppKitTheme.colors.inverse100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_mobile), @@ -272,7 +272,7 @@ internal fun MobileIcon( @Composable internal fun WebIcon( - tint: Color = Web3ModalTheme.colors.inverse100 + tint: Color = AppKitTheme.colors.inverse100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_browser), @@ -284,7 +284,7 @@ internal fun WebIcon( @Composable internal fun ForwardIcon( - tint: Color = Web3ModalTheme.colors.inverse100 + tint: Color = AppKitTheme.colors.inverse100 ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_chevron_right), diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Label.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Label.kt new file mode 100644 index 000000000..9aed4fd03 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Label.kt @@ -0,0 +1,129 @@ +package com.reown.appkit.ui.components.internal.commons + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun AllLabel(isEnabled: Boolean = true) { + ListLabel(text = "ALL", isEnabled = isEnabled) +} + +@Composable +internal fun TextLabel(text: String, isEnabled: Boolean = true) { + ListLabel( + text = text, + isEnabled = isEnabled, + backgroundColor = AppKitTheme.colors.grayGlass10, + labelTextColor = AppKitTheme.colors.foreground.color150 + ) +} + +@Composable +internal fun GetWalletLabel(isEnabled: Boolean = true) { + ListLabel(text = "GET WALLET", isEnabled = isEnabled) +} + +@Composable +internal fun RecentLabel(isEnabled: Boolean = true) { + ListLabel( + text = "RECENT", + isEnabled = isEnabled, + backgroundColor = AppKitTheme.colors.grayGlass10, + labelTextColor = AppKitTheme.colors.foreground.color150 + ) +} + +@Composable +internal fun InstalledLabel(isEnabled: Boolean = true) { + ListLabel( + text = "INSTALLED", + isEnabled = isEnabled, + backgroundColor = AppKitTheme.colors.success15, + labelTextColor = AppKitTheme.colors.success + ) +} + +@Composable +private fun ListLabel( + text: String, + isEnabled: Boolean, + backgroundColor: Color = AppKitTheme.colors.accent15, + labelTextColor: Color = AppKitTheme.colors.accent100 +) { + val textColor: Color + val background: Color + if (isEnabled) { + background = backgroundColor + textColor = labelTextColor + } else { + background = AppKitTheme.colors.grayGlass10 + textColor = AppKitTheme.colors.foreground.color300 + } + Box( + modifier = Modifier + .height(20.dp) + .background(background, shape = RoundedCornerShape(4.dp)) + .padding( horizontal = 5.dp), + contentAlignment = Alignment.Center + ) { + Text(text = text, style = AppKitTheme.typo.micro700.copy(textColor)) + } +} + +@Composable +@UiModePreview +private fun AllLabelPreview() { + MultipleComponentsPreview( + { AllLabel() }, + { AllLabel(false) }, + ) +} + +@Composable +@UiModePreview +private fun TextLabelPreview() { + MultipleComponentsPreview( + { TextLabel("240+") }, + { TextLabel("240+", false) }, + ) +} + +@Composable +@UiModePreview +private fun GetWalletLabelPreview() { + MultipleComponentsPreview( + { GetWalletLabel() }, + { GetWalletLabel(false) }, + ) +} + +@Composable +@UiModePreview +private fun RecentLabelPreview() { + MultipleComponentsPreview( + { RecentLabel() }, + { RecentLabel(false) }, + ) +} + +@Composable +@UiModePreview +private fun InstalledLabelPreview() { + MultipleComponentsPreview( + { InstalledLabel() }, + { InstalledLabel(false) }, + ) +} + diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ListSelect.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ListSelect.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ListSelect.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ListSelect.kt index fb8bf425d..f7ed3088b 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/ListSelect.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/ListSelect.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -15,10 +15,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.walletconnect.WalletConnectLogo -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.components.internal.walletconnect.WalletConnectLogo +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun ListSelectRow( @@ -33,11 +33,11 @@ internal fun ListSelectRow( val background: Color val textColor: Color if (isEnabled) { - background = Web3ModalTheme.colors.grayGlass02 - textColor = Web3ModalTheme.colors.foreground.color100 + background = AppKitTheme.colors.grayGlass02 + textColor = AppKitTheme.colors.foreground.color100 } else { - background = Web3ModalTheme.colors.grayGlass15 - textColor = Web3ModalTheme.colors.foreground.color300 + background = AppKitTheme.colors.grayGlass15 + textColor = AppKitTheme.colors.foreground.color300 } Surface( color = Color.Transparent, @@ -57,7 +57,7 @@ internal fun ListSelectRow( HorizontalSpacer(width = 10.dp) Text( text = text, - style = Web3ModalTheme.typo.paragraph500.copy(textColor), + style = AppKitTheme.typo.paragraph500.copy(textColor), modifier = Modifier.weight(1f) ) label?.let { diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Loading.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Loading.kt similarity index 91% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Loading.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Loading.kt index a01570a91..34d8ec019 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Loading.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Loading.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode @@ -35,16 +35,16 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.network.HexagonShape -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.components.internal.commons.network.HexagonShape +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun LoadingSpinner( strokeWidth: Dp = 4.dp, size: Dp = 24.dp, - tint: Color = Web3ModalTheme.colors.accent100 + tint: Color = AppKitTheme.colors.accent100 ) { CircularProgressIndicator( strokeWidth = strokeWidth, @@ -93,7 +93,7 @@ private fun Modifier.drawAnimatedBorder( shape: Shape, durationMillis: Int ) = composed { - val loaderColor = Web3ModalTheme.colors.accent100 + val loaderColor = AppKitTheme.colors.accent100 var pathLenght by remember { mutableFloatStateOf(0f) } val infiniteTransition = rememberInfiniteTransition(label = "rotation") val loaderProgress by infiniteTransition.animateFloat( diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Spacers.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Spacers.kt new file mode 100644 index 000000000..0562b08c4 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Spacers.kt @@ -0,0 +1,26 @@ +package com.reown.appkit.ui.components.internal.commons + +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp + +@Composable +internal fun VerticalSpacer(height: Dp) { + Spacer(modifier = Modifier.height(height)) +} + +@Composable +internal fun HorizontalSpacer(width: Dp) { + Spacer(modifier = Modifier.width(width)) +} + +@Composable +internal fun RowScope.WeightSpacer(weight: Float = 1f) { + Spacer(modifier = Modifier.weight(weight)) +} + +@Composable +internal fun ColumnScope.WeightSpacer(weight: Float = 1f) { + Spacer(modifier = Modifier.weight(weight)) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Surface.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Surface.kt similarity index 87% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Surface.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Surface.kt index b8f98cd94..96812c9a4 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Surface.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Surface.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons +package com.reown.appkit.ui.components.internal.commons import androidx.compose.material.Surface import androidx.compose.runtime.Composable diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/TestTags.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/TestTags.kt new file mode 100644 index 000000000..0fef15179 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/TestTags.kt @@ -0,0 +1,5 @@ +package com.reown.appkit.ui.components.internal.commons + +internal object TestTags { + const val TITLE = "Title" +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Wallets.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Wallets.kt new file mode 100644 index 000000000..6fa0b6312 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/Wallets.kt @@ -0,0 +1,193 @@ +package com.reown.appkit.ui.components.internal.commons + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.grid.LazyGridScope +import androidx.compose.foundation.lazy.grid.itemsIndexed +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.appkit.R +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.grayColorFilter +import com.reown.appkit.utils.imageHeaders + +@Composable +internal fun MultipleWalletIcon(wallets: List) { + Column( + modifier = Modifier + .size(40.dp) + .background(AppKitTheme.colors.background.color200, shape = RoundedCornerShape(10.dp)) + .padding(1.dp) + .border(1.dp, AppKitTheme.colors.grayGlass10, shape = RoundedCornerShape(10.dp)), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + wallets.chunked(2).forEach { + Row( + modifier = Modifier.width(40.dp), + horizontalArrangement = Arrangement.Center + ) { + it.forEach { item -> + WalletImage( + url = item.imageUrl, + modifier = Modifier + .size(16.dp) + .padding(1.dp) + .clip(RoundedCornerShape(4.dp)) + ) + } + } + } + + } +} + +internal fun LazyGridScope.walletsGridItems( + wallets: List, + onWalletItemClick: (Wallet) -> Unit +) { + itemsIndexed( + items = wallets, + key = { _, wallet -> wallet.id } + ) { _, wallet -> + WalletGridItem( + wallet = wallet, + onWalletItemClick = onWalletItemClick + ) + } +} + +@Composable +internal fun WalletImageWithLoader(url: String?) { + LoadingBorder( + cornerRadius = 28.dp + ) { + RoundedWalletImage(url = url) + } +} + +@Composable +internal fun RoundedWalletImage(url: String?) { + WalletImage( + url = url, modifier = Modifier + .size(80.dp) + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass10, shape = RoundedCornerShape(28.dp)) + .clip(RoundedCornerShape(28.dp)) + ) +} + +@Composable +internal fun WalletImage(url: String?, isEnabled: Boolean = true, modifier: Modifier) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(url) + .fallback(R.drawable.walletconnect_blue) + .crossfade(true) + .placeholder(R.drawable.wallet_placeholder) + .imageHeaders() + .build(), + contentDescription = null, + modifier = modifier, + colorFilter = if (isEnabled) null else grayColorFilter + ) +} + +@Composable +internal fun BoxScope.InstalledWalletIcon() { + Icon( + modifier = Modifier + .offset(x = 2.dp, y = 2.dp) + .background(AppKitTheme.colors.background.color125, shape = CircleShape) + .align(Alignment.BottomEnd) + .background(AppKitTheme.colors.grayGlass02, shape = CircleShape) + .padding(2.dp) + .size(12.dp) + .background(AppKitTheme.colors.success.copy(0.15f), shape = CircleShape) + .padding(2.dp), + imageVector = ImageVector.vectorResource(id = R.drawable.ic_check), + contentDescription = "WalletConnectLogo", + tint = AppKitTheme.colors.success + ) +} + +@Composable +internal fun WalletGridItem( + wallet: Wallet, + onWalletItemClick: (Wallet) -> Unit +) { + TransparentSurface( + modifier = Modifier.padding(2.dp), + shape = RoundedCornerShape(16.dp) + ) { + Column( + modifier = Modifier + .width(76.dp) + .height(96.dp) + .background(AppKitTheme.colors.grayGlass02) + .clickable { onWalletItemClick(wallet) }, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Box { + WalletImage( + url = wallet.imageUrl, + modifier = Modifier + .size(56.dp) + .clip(RoundedCornerShape(16.dp)) + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass10, shape = RoundedCornerShape(16.dp)) + ) + if (wallet.isWalletInstalled) { + InstalledWalletIcon() + } + } + VerticalSpacer(height = 8.dp) + Text( + text = wallet.name, + style = AppKitTheme.typo.tiny500, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = 2.dp) + ) + } + } +} + +@UiModePreview +@Composable +private fun PreviewWallets() { + MultipleComponentsPreview( + { WalletGridItem(wallet = testWallets.first(), onWalletItemClick = {}) }, + { WalletGridItem(wallet = testWallets[1], onWalletItemClick = {}) }, + { MultipleWalletIcon(wallets = testWallets.take(4)) }, + ) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountImage.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountImage.kt similarity index 84% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountImage.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountImage.kt index d3aa56a16..1936205c4 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountImage.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountImage.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.account +package com.reown.appkit.ui.components.internal.commons.account import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -14,9 +14,9 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme import kotlin.math.roundToInt @Composable @@ -26,7 +26,7 @@ internal fun AccountImage(address: String, avatarUrl: String?) { } else { Box( modifier = Modifier - .border(width = 8.dp, color = Web3ModalTheme.colors.grayGlass05, shape = CircleShape) + .border(width = 8.dp, color = AppKitTheme.colors.grayGlass05, shape = CircleShape) .padding(8.dp) .size(64.dp) .background(brush = Brush.linearGradient(generateAvatarColors(address)), shape = CircleShape) @@ -38,7 +38,7 @@ internal fun AccountImage(address: String, avatarUrl: String?) { private fun AccountAvatar(url: String) { Box( modifier = Modifier - .border(width = 8.dp, color = Web3ModalTheme.colors.grayGlass05, shape = CircleShape) + .border(width = 8.dp, color = AppKitTheme.colors.grayGlass05, shape = CircleShape) .padding(8.dp) .size(64.dp) ) { diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountName.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountName.kt new file mode 100644 index 000000000..6a07b0850 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/account/AccountName.kt @@ -0,0 +1,55 @@ +package com.reown.appkit.ui.components.internal.commons.account + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ClipboardManager +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp +import com.reown.modal.ui.components.common.roundedClickable +import com.reown.appkit.domain.model.AccountData +import com.reown.appkit.domain.model.Identity +import com.reown.appkit.ui.components.internal.commons.CopyIcon +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.accountDataPreview +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.toVisibleAddress + +@Composable +internal fun AccountName(accountData: AccountData) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + val clipboardManager: ClipboardManager = LocalClipboardManager.current + val name = accountData.identity?.name ?: accountData.address.toVisibleAddress() + Text(text = name, style = AppKitTheme.typo.mediumTitle600) + CopyIcon( + modifier = Modifier + .padding(10.dp) + .size(16.dp) + .roundedClickable { clipboardManager.setText(AnnotatedString(accountData.address)) } + ) + } +} + +@UiModePreview +@Composable +private fun AccountAddressPreview() { + ComponentPreview { + AccountName(accountDataPreview) + } +} + +@UiModePreview +@Composable +private fun AccountNamePreview() { + ComponentPreview { + AccountName(accountDataPreview.copy(identity = Identity(name = "testIdentity.eth"))) + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/Button.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/Button.kt similarity index 89% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/Button.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/Button.kt index 5af18a62d..9e2e6cbe2 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/Button.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/Button.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.button +package com.reown.appkit.ui.components.internal.commons.button import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -21,13 +20,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.RetryIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletIcon -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.RetryIcon +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview @Composable internal fun TryAgainButton( diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ButtonStyle.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ButtonStyle.kt new file mode 100644 index 000000000..865297eeb --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ButtonStyle.kt @@ -0,0 +1,71 @@ +package com.reown.appkit.ui.components.internal.commons.button + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.theme.AppKitTheme + +internal data class ButtonData( + val size: ButtonSize, + val style: ButtonStyle, + val textStyle: TextStyle, + val tint: Color, + val background: Color +) + +internal enum class ButtonStyle { MAIN, ACCENT, SHADE, LOADING, ACCOUNT, LINK } + +internal enum class ButtonSize { M, S, ACCOUNT_M, ACCOUNT_S } + +@Composable +internal fun ButtonSize.getTextStyle() = when (this) { + ButtonSize.M, ButtonSize.ACCOUNT_M, ButtonSize.ACCOUNT_S -> AppKitTheme.typo.paragraph600 + ButtonSize.S -> AppKitTheme.typo.small600 +} + +@Composable +internal fun ButtonSize.getContentPadding() = when (this) { + ButtonSize.M -> PaddingValues(horizontal = 16.dp) + ButtonSize.S -> PaddingValues(horizontal = 12.dp) + ButtonSize.ACCOUNT_M -> PaddingValues(start = 8.dp, end = 12.dp) + ButtonSize.ACCOUNT_S -> PaddingValues(start = 6.dp, end = 12.dp) +} + +@Composable +internal fun ButtonSize.getHeight() = when (this) { + ButtonSize.M, ButtonSize.ACCOUNT_M -> 40.dp + ButtonSize.S, ButtonSize.ACCOUNT_S -> 32.dp +} + + +@Composable +internal fun ButtonStyle.getTextColor(isEnabled: Boolean) = when (this) { + ButtonStyle.MAIN -> if (isEnabled) AppKitTheme.colors.inverse100 else AppKitTheme.colors.foreground.color300 + ButtonStyle.ACCENT, ButtonStyle.LOADING -> if (isEnabled) AppKitTheme.colors.accent100 else AppKitTheme.colors.grayGlass20 + ButtonStyle.SHADE -> if (isEnabled) AppKitTheme.colors.foreground.color150 else AppKitTheme.colors.grayGlass15 + ButtonStyle.ACCOUNT -> if (isEnabled) AppKitTheme.colors.foreground.color100 else AppKitTheme.colors.grayGlass15 + ButtonStyle.LINK -> if(isEnabled) AppKitTheme.colors.foreground.color200 else AppKitTheme.colors.grayGlass15 +} + +@Composable +internal fun ButtonStyle.getBackgroundColor(isEnabled: Boolean) = when (this) { + ButtonStyle.MAIN -> if (isEnabled) AppKitTheme.colors.accent100 else AppKitTheme.colors.grayGlass20 + ButtonStyle.ACCENT -> if (isEnabled) Color.Transparent else AppKitTheme.colors.grayGlass10 + ButtonStyle.SHADE, ButtonStyle.LINK -> if (isEnabled) Color.Transparent else AppKitTheme.colors.grayGlass05 + ButtonStyle.LOADING -> AppKitTheme.colors.grayGlass10 + ButtonStyle.ACCOUNT -> if (isEnabled) AppKitTheme.colors.grayGlass10 else AppKitTheme.colors.grayGlass15 +} + +@Composable +internal fun ButtonStyle.getBorder(isEnabled: Boolean) = when (this) { + ButtonStyle.MAIN, ButtonStyle.LINK -> if (isEnabled) Color.Transparent else AppKitTheme.colors.grayGlass20 + ButtonStyle.ACCENT, ButtonStyle.SHADE, ButtonStyle.LOADING, ButtonStyle.ACCOUNT -> if (isEnabled) AppKitTheme.colors.grayGlass10 else AppKitTheme.colors.grayGlass05 +} + +internal data class ButtonPreview( + val style: ButtonStyle, + val size: ButtonSize, + val isEnabled: Boolean +) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ChipButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ChipButton.kt similarity index 88% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ChipButton.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ChipButton.kt index ae20a5cbc..30f06891b 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ChipButton.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/ChipButton.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.button +package com.reown.appkit.ui.components.internal.commons.button import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -20,11 +20,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.CompassIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview +import com.reown.appkit.ui.components.internal.commons.CompassIcon +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview @Composable internal fun ChipButton( diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/LinkButton.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/LinkButton.kt similarity index 79% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/LinkButton.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/LinkButton.kt index a4e92c64f..59edbc6d0 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/LinkButton.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/button/LinkButton.kt @@ -1,10 +1,10 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.button +package com.reown.appkit.ui.components.internal.commons.button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer @Composable internal fun LinkButton( diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/AccountEntry.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/AccountEntry.kt similarity index 81% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/AccountEntry.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/AccountEntry.kt index 7e558cd40..ece50fbbe 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/AccountEntry.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/AccountEntry.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.entry +package com.reown.appkit.ui.components.internal.commons.entry import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -13,14 +13,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ChevronRightIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.network.CircleNetworkImage -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ChevronRightIcon +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.network.CircleNetworkImage +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme internal enum class AccountEntryState { LOADING, NEXT, INFO @@ -62,7 +62,7 @@ internal fun AccountEntry( @UiModePreview @Composable private fun AccountEntryPreview() { - val content: @Composable (EntryColors) -> Unit = { Text(text = "Account entry", style = Web3ModalTheme.typo.paragraph500.copy(color = it.textColor))} + val content: @Composable (EntryColors) -> Unit = { Text(text = "Account entry", style = AppKitTheme.typo.paragraph500.copy(color = it.textColor))} MultipleComponentsPreview( { AccountEntry( diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/ActionEntry.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/ActionEntry.kt new file mode 100644 index 000000000..448aa2e54 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/ActionEntry.kt @@ -0,0 +1,90 @@ +package com.reown.appkit.ui.components.internal.commons.entry + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.CopyIcon +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.LinkButton +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun ActionEntry( + text: String, + modifier: Modifier = Modifier, + icon: @Composable() ((Color) -> Unit)? = null, + isEnabled: Boolean = true, + contentPadding: PaddingValues = PaddingValues(0.dp), + onClick: () -> Unit +) { + BaseEntry( + isEnabled = isEnabled, + contentPadding = contentPadding + ) { colors -> + Row( + modifier = modifier + .clickable { onClick() } + .background(colors.background) + .padding(horizontal = 16.dp, vertical = 12.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + icon?.let { + it(colors.textColor) + HorizontalSpacer(width = 8.dp) + } + Text(text = text, style = AppKitTheme.typo.paragraph500.copy(color = colors.textColor)) + } + } +} + +@Composable +internal fun CopyActionEntry(isEnabled: Boolean = true, onClick: () -> Unit) { + LinkButton( + text = "Copy link", + startIcon = { + CopyIcon(tint = it, modifier = Modifier.size(12.dp)) + }, + isEnabled = isEnabled, + size = ButtonSize.S, + onClick = onClick + ) +} + +@UiModePreview +@Composable +private fun ActionEntryPreview() { + MultipleComponentsPreview( + { ActionEntry(text = "Action without icon") {} }, + { ActionEntry(text = "Action without icon", isEnabled = false) {} }, + { ActionEntry(icon = { Image(imageVector = ImageVector.vectorResource(R.drawable.ic_check), contentDescription = null, colorFilter = ColorFilter.tint(it)) }, text = "Action with icon") {} }, + { ActionEntry(isEnabled = false, icon = { Image(imageVector = ImageVector.vectorResource(R.drawable.ic_check), contentDescription = null, colorFilter = ColorFilter.tint(it)) }, text = "Action with icon") {} }, + ) +} + +@UiModePreview +@Composable +private fun CopyActionPreview() { + MultipleComponentsPreview( + { CopyActionEntry {} }, + { CopyActionEntry(isEnabled = false) {} } + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/BaseEntry.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/BaseEntry.kt new file mode 100644 index 000000000..bedf8f14b --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/BaseEntry.kt @@ -0,0 +1,45 @@ +package com.reown.appkit.ui.components.internal.commons.entry + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.TransparentSurface +import com.reown.appkit.ui.theme.AppKitTheme + +internal data class EntryColors( + val background: Color, + val textColor: Color, + val secondaryColor: Color +) + +@Composable +internal fun BaseEntry( + isEnabled: Boolean, + contentPadding: PaddingValues = PaddingValues(0.dp), + content: @Composable (EntryColors) -> Unit +) { + val background: Color + val textColor: Color + val secondaryColor: Color + if (isEnabled) { + background = AppKitTheme.colors.grayGlass02 + textColor = AppKitTheme.colors.foreground.color100 + secondaryColor = AppKitTheme.colors.foreground.color200 + } else { + background = AppKitTheme.colors.grayGlass15 + textColor = AppKitTheme.colors.grayGlass15 + secondaryColor = AppKitTheme.colors.grayGlass15 + } + val entryColors = EntryColors(background, textColor, secondaryColor) + + TransparentSurface( + shape = RoundedCornerShape(16.dp), + modifier = Modifier.padding(contentPadding) + ) { + content(entryColors) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/StoreEntry.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/StoreEntry.kt new file mode 100644 index 000000000..889dbd4fb --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/entry/StoreEntry.kt @@ -0,0 +1,89 @@ +package com.reown.appkit.ui.components.internal.commons.entry + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.ForwardIcon +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.TransparentSurface +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun StoreEntry( + text: String, + isEnabled: Boolean = true, + onClick: () -> Unit +) { + val background: Color + val textColor: Color + if (isEnabled) { + textColor = AppKitTheme.colors.foreground.color200 + background = AppKitTheme.colors.grayGlass02 + } else { + textColor = AppKitTheme.colors.foreground.color300 + background = AppKitTheme.colors.grayGlass10 + } + Surface( + color = Color.Transparent, + shape = RoundedCornerShape(16.dp), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .background(background) + .padding(horizontal = 18.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = text, style = AppKitTheme.typo.paragraph500.copy(color = textColor), modifier = Modifier.weight(1f)) + HorizontalSpacer(width = 10.dp) + GetButton(onClick) + } + } +} + +@Composable +private fun GetButton(onClick: () -> Unit) { + TransparentSurface(shape = RoundedCornerShape(100)) { + Row( + modifier = Modifier + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass10, shape = CircleShape) + .clickable { onClick() } + .padding(start = 12.dp, end = 8.dp, top = 6.dp, bottom = 6.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Text(text = "Get", style = AppKitTheme.typo.small500.copy(color = AppKitTheme.colors.accent100)) + HorizontalSpacer(width = 4.dp) + ForwardIcon(tint = AppKitTheme.colors.accent100) + } + } +} + +@UiModePreview +@Composable +private fun PreviewStoreEntry() { + val wallet = testWallets.first() + MultipleComponentsPreview( + { StoreEntry(text = "Don't have ${wallet.name}?") {} }, + { StoreEntry(text = "Don't have ${wallet.name}?", isEnabled = false) {} }, + ) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/BaseTextInput.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/BaseTextInput.kt new file mode 100644 index 000000000..cf7b88ca1 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/BaseTextInput.kt @@ -0,0 +1,67 @@ +package com.reown.appkit.ui.components.internal.commons.inputs + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun BaseTextInput( + inputState: InputState, + modifier: Modifier = Modifier, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + isEnabled: Boolean = true, + content: @Composable (innerTextField: @Composable () -> Unit, inputData: InputData) -> Unit +) { + val focusRequester = remember { FocusRequester() } + val borderColor: Color + val backgroundColor: Color + val state by inputState.state.collectAsState() + + when { + state.isFocused -> { + borderColor = AppKitTheme.colors.accent100 + backgroundColor = AppKitTheme.colors.grayGlass10 + } + + isEnabled -> { + borderColor = AppKitTheme.colors.grayGlass05 + backgroundColor = AppKitTheme.colors.grayGlass05 + } + + else -> { + borderColor = AppKitTheme.colors.grayGlass10 + backgroundColor = AppKitTheme.colors.grayGlass15 + } + } + + BasicTextField(value = state.text, + onValueChange = inputState::onTextChange, + textStyle = AppKitTheme.typo.paragraph400.copy(color = AppKitTheme.colors.foreground.color100), + cursorBrush = SolidColor(AppKitTheme.colors.accent100), + singleLine = true, + keyboardActions = KeyboardActions { inputState.submit(state.text) }, + keyboardOptions = keyboardOptions, + modifier = modifier + .background(color = backgroundColor, shape = RoundedCornerShape(12.dp)) + .border(width = 1.dp, color = borderColor, shape = RoundedCornerShape(12.dp)) + .onFocusChanged { inputState.onFocusChange(it.hasFocus) } + .focusRequester(focusRequester), + decorationBox = { + content(it, state) + }) +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputCancel.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputCancel.kt new file mode 100644 index 000000000..ae3656694 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputCancel.kt @@ -0,0 +1,60 @@ +package com.reown.appkit.ui.components.internal.commons.inputs + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun InputCancel( + modifier: Modifier = Modifier, + isEnabled: Boolean = true, + onClick: () -> Unit +) { + val background: Color + val tint: Color + + if (isEnabled) { + tint = AppKitTheme.colors.background.color100 + background = AppKitTheme.colors.grayGlass20 + } else { + tint = AppKitTheme.colors.background.color100 + background = AppKitTheme.colors.grayGlass10 + } + + Surface( + color = background, + modifier = Modifier.size(18.dp).clickable { onClick() }.then(modifier), + shape = RoundedCornerShape(6.dp) + ) { + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_close), + contentDescription = ContentDescription.CLEAR.description, + modifier = Modifier.size(10.dp).padding(4.dp), + colorFilter = ColorFilter.tint(tint) + ) + } +} + +@UiModePreview +@Composable +private fun PreviewInputCancel() { + MultipleComponentsPreview( + { InputCancel() {} }, + { InputCancel(isEnabled = false) {} }, + ) +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputState.kt similarity index 92% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputState.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputState.kt index 63bb09bce..dd8a48c26 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputState.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/InputState.kt @@ -1,7 +1,7 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.inputs +package com.reown.appkit.ui.components.internal.commons.inputs import androidx.compose.ui.focus.FocusManager -import com.walletconnect.util.Empty +import com.reown.util.Empty import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/SearchInput.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/SearchInput.kt similarity index 81% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/SearchInput.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/SearchInput.kt index 9ea186115..9764a5355 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/SearchInput.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/inputs/SearchInput.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.inputs +package com.reown.appkit.ui.components.internal.commons.inputs import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -31,13 +31,13 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.modal.ui.components.common.HorizontalSpacer +import com.reown.util.Empty +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -95,24 +95,24 @@ internal fun SearchInput( when { searchState.isFocused -> { - borderColor = Web3ModalTheme.colors.accent100 - backgroundColor = Web3ModalTheme.colors.grayGlass10 + borderColor = AppKitTheme.colors.accent100 + backgroundColor = AppKitTheme.colors.grayGlass10 } isEnabled -> { - borderColor = Web3ModalTheme.colors.grayGlass05 - backgroundColor = Web3ModalTheme.colors.grayGlass05 + borderColor = AppKitTheme.colors.grayGlass05 + backgroundColor = AppKitTheme.colors.grayGlass05 } else -> { - borderColor = Web3ModalTheme.colors.grayGlass10 - backgroundColor = Web3ModalTheme.colors.grayGlass15 + borderColor = AppKitTheme.colors.grayGlass10 + backgroundColor = AppKitTheme.colors.grayGlass15 } } BasicTextField( value = state.searchValue, onValueChange = searchState::onSearchValueChange, - textStyle = Web3ModalTheme.typo.paragraph400.copy(color = Web3ModalTheme.colors.foreground.color100), - cursorBrush = SolidColor(Web3ModalTheme.colors.accent100), + textStyle = AppKitTheme.typo.paragraph400.copy(color = AppKitTheme.colors.foreground.color100), + cursorBrush = SolidColor(AppKitTheme.colors.accent100), singleLine = true, keyboardActions = KeyboardActions(onSearch = { searchState.onSearchSubmit() @@ -132,7 +132,7 @@ internal fun SearchInput( ) { HorizontalSpacer(width = 10.dp) Icon( - tint = Web3ModalTheme.colors.foreground.color275, + tint = AppKitTheme.colors.foreground.color275, modifier = Modifier.size(14.dp), imageVector = ImageVector.vectorResource(id = R.drawable.ic_search), contentDescription = ContentDescription.SEARCH.description, @@ -140,7 +140,7 @@ internal fun SearchInput( HorizontalSpacer(width = 6.dp) Box(modifier = Modifier.weight(1f)) { if (state.searchValue.isBlank()) { - Text(text = "Search wallets", style = Web3ModalTheme.typo.paragraph400.copy(color = Web3ModalTheme.colors.foreground.color275)) + Text(text = "Search wallets", style = AppKitTheme.typo.paragraph400.copy(color = AppKitTheme.colors.foreground.color275)) } innerTextField() if (state.searchValue.isNotBlank()) { diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkIcons.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkIcons.kt similarity index 90% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkIcons.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkIcons.kt index 58612edff..c743cb4f9 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkIcons.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkIcons.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.network +package com.reown.appkit.ui.components.internal.commons.network import android.graphics.drawable.Drawable import androidx.compose.foundation.border @@ -28,12 +28,12 @@ import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import coil.compose.AsyncImage import coil.request.ImageRequest -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.grayColorFilter -import com.walletconnect.web3.modal.utils.imageHeaders +import com.reown.appkit.R +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.grayColorFilter +import com.reown.appkit.utils.imageHeaders import kotlin.math.cos import kotlin.math.sin @@ -47,7 +47,7 @@ internal fun CircleNetworkImage( Box( modifier = Modifier .size(size) - .border(width = 2.dp, color = Web3ModalTheme.colors.grayGlass05, shape = CircleShape) + .border(width = 2.dp, color = AppKitTheme.colors.grayGlass05, shape = CircleShape) .padding(2.dp) ) { AsyncImage( @@ -72,7 +72,7 @@ internal fun HexagonNetworkImage( borderColor: Color? = null, placeholder: Drawable? = null ) { - val overlayColor = Web3ModalTheme.colors.grayGlass10 + val overlayColor = AppKitTheme.colors.grayGlass10 AsyncImage( model = ImageRequest.Builder(LocalContext.current) .data(data) diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkItem.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkItem.kt new file mode 100644 index 000000000..caa6b23ba --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/network/ChainNetworkItem.kt @@ -0,0 +1,101 @@ +package com.reown.appkit.ui.components.internal.commons.network + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.client.Modal +import com.reown.appkit.ui.components.internal.commons.TransparentSurface +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun ChainNetworkItem( + image: Modal.Model.ChainImage, + isSelected: Boolean, + networkName: String, + isEnabled: Boolean = true, + onItemClick: () -> Unit, +) { + val data = when (image) { + is Modal.Model.ChainImage.Asset -> image.id + is Modal.Model.ChainImage.Network -> image.url + } + val backgroundColor: Color + val textColor: Color + val borderColor: Color? + when { + isSelected -> { + backgroundColor = AppKitTheme.colors.accent10 + textColor = AppKitTheme.colors.accent100 + borderColor = AppKitTheme.colors.accent100 + } + + isEnabled -> { + backgroundColor = AppKitTheme.colors.grayGlass02 + textColor = AppKitTheme.colors.foreground.color100 + borderColor = null + } + + else -> { + backgroundColor = AppKitTheme.colors.grayGlass10 + textColor = AppKitTheme.colors.grayGlass15 + borderColor = null + } + } + TransparentSurface( + shape = RoundedCornerShape(16.dp), + modifier = Modifier.padding(2.dp) + ) { + Column( + modifier = Modifier + .width(76.dp) + .height(96.dp) + .background(backgroundColor) + .clickable(isEnabled) { onItemClick() }, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + HexagonNetworkImage( + data = data, + isEnabled = isEnabled, + borderColor = borderColor, + ) + VerticalSpacer(height = 8.dp) + Text( + text = networkName, + style = AppKitTheme.typo.tiny500.copy(textColor), + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = 2.dp) + ) + } + } +} + +@UiModePreview +@Composable +private fun ChainNetworkItemPreview() { + val image = Modal.Model.ChainImage.Asset(R.drawable.system) + MultipleComponentsPreview( + { ChainNetworkItem(image = image, isSelected = true, isEnabled = true, networkName = "TestNetwork", onItemClick = {}) }, + { ChainNetworkItem(image = image, isSelected = false, isEnabled = true, networkName = "TestNetwork", onItemClick = {}) }, + { ChainNetworkItem(image = image, isSelected = false, isEnabled = false, networkName = "TestNetwork", onItemClick = {}) } + ) +} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/switch/PlatformTabRow.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/switch/PlatformTabRow.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/switch/PlatformTabRow.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/switch/PlatformTabRow.kt index 9fd46a210..a568aa72d 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/switch/PlatformTabRow.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/commons/switch/PlatformTabRow.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.switch +package com.reown.appkit.ui.components.internal.commons.switch import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.spring @@ -15,12 +15,10 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon import androidx.compose.material.Tab import androidx.compose.material.TabPosition import androidx.compose.material.TabRow @@ -31,21 +29,16 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.MobileIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.WebIcon -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.MobileIcon +import com.reown.appkit.ui.components.internal.commons.WebIcon +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme internal enum class PlatformTab( val value: Int, val label: String @@ -70,13 +63,13 @@ internal fun PlatformTabRow( TabRow( selectedTabIndex = platformTab.value, backgroundColor = Color.Transparent, - contentColor = Web3ModalTheme.colors.foreground.color200, + contentColor = AppKitTheme.colors.foreground.color200, indicator = indicator, divider = {}, modifier = Modifier .width(210.dp) .height(32.dp) - .background(color = Web3ModalTheme.colors.grayGlass02, shape = RoundedCornerShape(80f)) + .background(color = AppKitTheme.colors.grayGlass02, shape = RoundedCornerShape(80f)) ) { PlatformTab.values().forEach { val isSelected = platformTab == it @@ -94,7 +87,7 @@ internal fun PlatformTabRow( @Composable private fun TabContent(platform: PlatformTab, isSelected: Boolean) { - val color = if (isSelected) Web3ModalTheme.colors.foreground.color100 else Web3ModalTheme.colors.foreground.color200 + val color = if (isSelected) AppKitTheme.colors.foreground.color100 else AppKitTheme.colors.foreground.color200 Row( modifier = Modifier.fillMaxHeight(), verticalAlignment = Alignment.CenterVertically, @@ -104,7 +97,7 @@ private fun TabContent(platform: PlatformTab, isSelected: Boolean) { HorizontalSpacer(width = 4.dp) Text( text = platform.label, - style = Web3ModalTheme.typo.small500.copy(color = color, textAlign = TextAlign.Center), + style = AppKitTheme.typo.small500.copy(color = color, textAlign = TextAlign.Center), modifier = Modifier.wrapContentHeight() ) } @@ -147,8 +140,8 @@ private fun CustomIndicatorWithAnimation( .width(indicatorEnd - indicatorStart) .padding(3.dp) .fillMaxSize() - .background(color = Web3ModalTheme.colors.grayGlass02, RoundedCornerShape(50)) - .border(BorderStroke(1.dp, Web3ModalTheme.colors.grayGlass02), RoundedCornerShape(50)) + .background(color = AppKitTheme.colors.grayGlass02, RoundedCornerShape(50)) + .border(BorderStroke(1.dp, AppKitTheme.colors.grayGlass02), RoundedCornerShape(50)) .zIndex(1f) ) } diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/InputBox.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/InputBox.kt new file mode 100644 index 000000000..854f5d1df --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/InputBox.kt @@ -0,0 +1,41 @@ +package com.reown.appkit.ui.components.internal.email + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.inputs.InputState +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun InputValidationBox( + inputState: InputState, + errorMessage: String, + errorAlign: TextAlign = TextAlign.Left, + content: @Composable () -> Unit, +) { + val hasError by inputState.hasError.collectAsState() + + Column(modifier = Modifier.animateContentSize()) { + content() + if (hasError) { + VerticalSpacer(height = 4.dp) + Text( + text = errorMessage, + modifier = Modifier + .padding(horizontal = 14.dp) + .fillMaxWidth(), + style = AppKitTheme.typo.tiny400.copy(color = AppKitTheme.colors.error, textAlign = errorAlign) + ) + } + } +} + diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInput.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInput.kt similarity index 84% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInput.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInput.kt index aa2a828b8..f6fb3b592 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInput.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInput.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.email.code +package com.reown.appkit.ui.components.internal.email.code import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.RepeatMode @@ -37,8 +37,8 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.util.Empty +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun CodeInput(codeInputState: CodeInputState) { @@ -52,8 +52,8 @@ internal fun CodeInput(codeInputState: CodeInputState) { BasicTextField( value = state.text, onValueChange = codeInputState::onTextChange, - textStyle = Web3ModalTheme.typo.paragraph400.copy(color = Web3ModalTheme.colors.foreground.color100), - cursorBrush = SolidColor(Web3ModalTheme.colors.accent100), + textStyle = AppKitTheme.typo.paragraph400.copy(color = AppKitTheme.colors.foreground.color100), + cursorBrush = SolidColor(AppKitTheme.colors.accent100), singleLine = true, keyboardActions = KeyboardActions { codeInputState.submit(state.text) }, keyboardOptions = KeyboardOptions( @@ -91,15 +91,15 @@ private fun CodeDigitText( val borderColor: Color if (isFocused) { - background = Web3ModalTheme.colors.grayGlass10 - borderColor = Web3ModalTheme.colors.accent100 + background = AppKitTheme.colors.grayGlass10 + borderColor = AppKitTheme.colors.accent100 shadowModifier = Modifier .size(54.dp) - .border(4.dp, Web3ModalTheme.colors.accent20, RoundedCornerShape(20.dp)) + .border(4.dp, AppKitTheme.colors.accent20, RoundedCornerShape(20.dp)) .padding(4.dp) } else { - background = Web3ModalTheme.colors.grayGlass05 - borderColor = Web3ModalTheme.colors.grayGlass05 + background = AppKitTheme.colors.grayGlass05 + borderColor = AppKitTheme.colors.grayGlass05 shadowModifier = Modifier .size(54.dp) .padding(4.dp) @@ -126,13 +126,13 @@ private fun CodeDigitText( Box( modifier = Modifier .size(2.dp, 20.dp) - .background(Web3ModalTheme.colors.accent100) + .background(AppKitTheme.colors.accent100) ) } } else { Text( text = value, - style = Web3ModalTheme.typo.large400.copy(color = Web3ModalTheme.colors.grayGlass) + style = AppKitTheme.typo.large400.copy(color = AppKitTheme.colors.grayGlass) ) } } diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInputState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInputState.kt similarity index 87% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInputState.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInputState.kt index e3603e858..f357947b7 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/code/CodeInputState.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/code/CodeInputState.kt @@ -1,11 +1,11 @@ -package com.walletconnect.web3.modal.ui.components.internal.email.code +package com.reown.appkit.ui.components.internal.email.code import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.platform.LocalFocusManager -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.InputState +import com.reown.appkit.ui.components.internal.commons.inputs.InputState import kotlinx.coroutines.CoroutineScope @Composable diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInput.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInput.kt similarity index 75% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInput.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInput.kt index 65e18605e..dc8e70ee8 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInput.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInput.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.email.input +package com.reown.appkit.ui.components.internal.email.input import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -16,13 +16,13 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.modal.ui.components.common.roundedClickable -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.BaseTextInput -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.modal.ui.components.common.HorizontalSpacer +import com.reown.modal.ui.components.common.roundedClickable +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.inputs.BaseTextInput +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun EmailInput( @@ -45,7 +45,7 @@ internal fun EmailInput( ) { HorizontalSpacer(width = 10.dp) Icon( - tint = Web3ModalTheme.colors.foreground.color275, + tint = AppKitTheme.colors.foreground.color275, modifier = Modifier.size(14.dp), imageVector = ImageVector.vectorResource(id = R.drawable.ic_email), contentDescription = ContentDescription.EMAIL.description, @@ -53,7 +53,7 @@ internal fun EmailInput( HorizontalSpacer(width = 6.dp) Box(modifier = Modifier.weight(1f)) { if (inputData.text.isBlank()) { - Text(text = "Email", style = Web3ModalTheme.typo.paragraph400.copy(color = Web3ModalTheme.colors.foreground.color275)) + Text(text = "Email", style = AppKitTheme.typo.paragraph400.copy(color = AppKitTheme.colors.foreground.color275)) } innerTextField() } @@ -70,7 +70,7 @@ internal fun EmailInput( @Composable private fun ForwardIcon(onClick: () -> Unit) { Icon( - tint = Web3ModalTheme.colors.accent100, + tint = AppKitTheme.colors.accent100, modifier = Modifier .size(14.dp) .roundedClickable { onClick() }, diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInputState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInputState.kt similarity index 88% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInputState.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInputState.kt index ca3347fdc..7db84e0e7 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/input/EmailInputState.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/email/input/EmailInputState.kt @@ -1,11 +1,11 @@ -package com.walletconnect.web3.modal.ui.components.internal.email.input +package com.reown.appkit.ui.components.internal.email.input import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.platform.LocalFocusManager -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.InputState +import com.reown.appkit.ui.components.internal.commons.inputs.InputState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.update diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRoot.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRoot.kt new file mode 100644 index 000000000..5ff15d395 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRoot.kt @@ -0,0 +1,146 @@ +@file:OptIn(ExperimentalComposeUiApi::class) + +package com.reown.appkit.ui.components.internal.root + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import com.reown.modal.ui.components.common.roundedClickable +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.ui.components.internal.AppKitTopBar +import com.reown.appkit.ui.components.internal.commons.BackArrowIcon +import com.reown.appkit.ui.components.internal.commons.FullWidthDivider +import com.reown.appkit.ui.components.internal.commons.QuestionMarkIcon +import com.reown.appkit.ui.components.internal.snackbar.ModalSnackBarHost +import com.reown.appkit.ui.components.internal.snackbar.SnackBarState +import com.reown.appkit.ui.components.internal.snackbar.rememberSnackBarState +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import com.reown.appkit.ui.theme.AppKitTheme +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.onEach + +@Composable +internal fun AppKitRoot( + navController: NavHostController, + modifier: Modifier = Modifier, + closeModal: () -> Unit, + content: @Composable () -> Unit +) { + val scope = rememberCoroutineScope() + val rootState = rememberAppKitRootState(coroutineScope = scope, navController = navController) + val snackBarState = rememberSnackBarState(coroutineScope = scope) + val title by rootState.title.collectAsState(null) + + LaunchedEffect(Unit) { + AppKitDelegate + .wcEventModels + .filterIsInstance() + .onEach { event -> + snackBarState.showErrorSnack(event.throwable.localizedMessage ?: "Something went wrong") + } + .collect() + } + + Column( + verticalArrangement = Arrangement.Bottom, + modifier = modifier + ) { + ProvideAppKitThemeComposition { + AppKitRoot(rootState, snackBarState, title, closeModal, content) + } + } +} + +@Composable +internal fun AppKitRoot( + rootState: AppKitRootState, + snackBarState: SnackBarState, + title: String?, + closeModal: () -> Unit, + content: @Composable () -> Unit +) { + ModalSnackBarHost(snackBarState) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(AppKitTheme.colors.background.color125) + ) { + title?.let { title -> + AppKitTopBar( + title = title, + startIcon = { TopBarStartIcon(rootState) }, + onCloseIconClick = closeModal + ) + FullWidthDivider() + } + content() + } + } +} + +@Composable +private fun TopBarStartIcon( + rootState: AppKitRootState +) { + if (rootState.currentDestinationRoute == Route.SIWE_FALLBACK.path) { + questionMark(rootState) + } else { + if (rootState.canPopUp) { + val keyboardController = LocalSoftwareKeyboardController.current + BackArrowIcon(onClick = { + keyboardController?.hide() + rootState.popUp() + }) + } else { + when (rootState.currentDestinationRoute) { + Route.CONNECT_YOUR_WALLET.path -> questionMark(rootState) + } + } + } +} + +@Composable +private fun questionMark(rootState: AppKitRootState) { + QuestionMarkIcon( + modifier = Modifier + .size(36.dp) + .roundedClickable(onClick = rootState::navigateToHelp) + .padding(10.dp) + ) +} + +@Composable +@UiModePreview +private fun PreviewAppKitRoot() { + val content: @Composable () -> Unit = { Box(modifier = Modifier.size(200.dp)) } + val scope = rememberCoroutineScope() + val navController = rememberNavController() + val rootState = rememberAppKitRootState(coroutineScope = scope, navController = navController) + val snackBarState = rememberSnackBarState(coroutineScope = scope) + + MultipleComponentsPreview( + { AppKitRoot(rootState, snackBarState, null, {}, { content() }) }, + { AppKitRoot(rootState, snackBarState, "Top Bar Title", {}, { content() }) } + ) +} + diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootState.kt new file mode 100644 index 000000000..34e02a278 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/root/AppKitRootState.kt @@ -0,0 +1,57 @@ +package com.reown.appkit.ui.components.internal.root + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavController +import androidx.navigation.NavDestination +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Props +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.getTitleArg +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +@Composable +internal fun rememberAppKitRootState( + coroutineScope: CoroutineScope, + navController: NavController +): AppKitRootState { + return remember(coroutineScope, navController) { + AppKitRootState(coroutineScope, navController) + } +} + +internal class AppKitRootState( + private val coroutineScope: CoroutineScope, + private val navController: NavController +) { + private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() + val currentDestinationFlow: Flow + get() = navController.currentBackStackEntryFlow + + val canPopUp: Boolean + get() = !topLevelDestinations.any { it.path == navController.currentDestination?.route } || navController.previousBackStackEntry != null + + val title: Flow = currentDestinationFlow + .map { it.getTitleArg() ?: it.destination.toTitle() } + + val currentDestinationRoute: String? + get() = navController.currentDestination?.route + + fun navigateToHelp() { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_WALLET_HELP,)) + navController.navigate(Route.WHAT_IS_WALLET.path) + } + + fun popUp() { + navController.popBackStack() + } +} + +private fun NavDestination.toTitle(): String? = Route.values().find { route.orEmpty().startsWith(it.path) }?.title + +private val topLevelDestinations = listOf(Route.CONNECT_YOUR_WALLET, Route.ACCOUNT, Route.CHOOSE_NETWORK, Route.CHANGE_NETWORK, Route.SIWE_FALLBACK) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackBar.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackBar.kt similarity index 79% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackBar.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackBar.kt index ef62b91da..51c4f3b1f 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackBar.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackBar.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.snackbar +package com.reown.appkit.ui.components.internal.snackbar import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background @@ -20,11 +20,11 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun ModalSnackBar( @@ -35,16 +35,16 @@ internal fun ModalSnackBar( modifier = Modifier .height(40.dp) .background( - color = Web3ModalTheme.colors.background.color125, + color = AppKitTheme.colors.background.color125, shape = shape ) .background( - color = Web3ModalTheme.colors.grayGlass05, + color = AppKitTheme.colors.grayGlass05, shape = shape ) .border( width = 1.dp, - color = Web3ModalTheme.colors.grayGlass05, + color = AppKitTheme.colors.grayGlass05, shape = shape ) .padding(8.dp) @@ -59,9 +59,9 @@ internal fun ModalSnackBar( SnackBarEventType.ERROR -> R.drawable.ic_error } val tint = when (snackBarEvent.type) { - SnackBarEventType.SUCCESS -> Web3ModalTheme.colors.success - SnackBarEventType.INFO -> Web3ModalTheme.colors.foreground.color200 - SnackBarEventType.ERROR -> Web3ModalTheme.colors.error + SnackBarEventType.SUCCESS -> AppKitTheme.colors.success + SnackBarEventType.INFO -> AppKitTheme.colors.foreground.color200 + SnackBarEventType.ERROR -> AppKitTheme.colors.error } Icon( @@ -73,7 +73,7 @@ internal fun ModalSnackBar( HorizontalSpacer(width = 8.dp) Text( text = snackBarEvent.message, - style = Web3ModalTheme.typo.paragraph500, + style = AppKitTheme.typo.paragraph500, modifier = Modifier.fillMaxHeight() ) HorizontalSpacer(width = 8.dp) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackbarHost.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackbarHost.kt similarity index 96% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackbarHost.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackbarHost.kt index 297017f32..1c0f2f157 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/ModalSnackbarHost.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/ModalSnackbarHost.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.snackbar +package com.reown.appkit.ui.components.internal.snackbar import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarEvent.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarEvent.kt similarity index 83% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarEvent.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarEvent.kt index 7df5d9d31..b564b9efc 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarEvent.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarEvent.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.snackbar +package com.reown.appkit.ui.components.internal.snackbar internal enum class SnackBarEventType { SUCCESS, INFO, ERROR diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarState.kt similarity index 97% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarState.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarState.kt index 227e7132a..625af7eea 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/snackbar/SnackBarState.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/snackbar/SnackBarState.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.components.internal.snackbar +package com.reown.appkit.ui.components.internal.snackbar import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectListSelect.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectListSelect.kt new file mode 100644 index 000000000..a92adcc1b --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectListSelect.kt @@ -0,0 +1,35 @@ +package com.reown.appkit.ui.components.internal.walletconnect + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import com.reown.appkit.ui.components.internal.commons.ListSelectRow +import com.reown.appkit.ui.components.internal.commons.AllWalletsIcon +import com.reown.appkit.ui.components.internal.commons.TextLabel +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview + +fun LazyListScope.allWallets(text: String, isEnabled: Boolean = true, onClick: () -> Unit) { + item { WalletConnectAll(text = text, isEnabled = isEnabled, onClick = onClick) } +} + +@Composable +private fun WalletConnectAll(text: String, isEnabled: Boolean = true, onClick: () -> Unit) { + ListSelectRow( + startIcon = { AllWalletsIcon() }, + text = "All wallets", + label = { TextLabel(text = text, isEnabled = isEnabled) }, + onClick = onClick, + contentPadding = PaddingValues(vertical = 4.dp) + ) +} + +@UiModePreview +@Composable +private fun WalletConnectListSelectPreview() { + MultipleComponentsPreview( + { WalletConnectAll(text = "240+") {} }, + { WalletConnectAll(text = "240+", isEnabled = false) {} }, + ) +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectLogo.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectLogo.kt new file mode 100644 index 000000000..5af89e875 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/components/internal/walletconnect/WalletConnectLogo.kt @@ -0,0 +1,59 @@ +package com.reown.appkit.ui.components.internal.walletconnect + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.previews.MultipleComponentsPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun WalletConnectLogo( + isEnabled: Boolean = true +) { + val background: Color + val border: Color + val colorFilter: ColorFilter? + if (isEnabled) { + background = AppKitTheme.colors.accent100 + border = AppKitTheme.colors.grayGlass10 + colorFilter = null + } else { + background = AppKitTheme.colors.background.color300 + border = AppKitTheme.colors.grayGlass05 + colorFilter = ColorFilter.tint(AppKitTheme.colors.grayGlass30) + } + + Image( + modifier = Modifier + .size(40.dp) + .background(background, shape = RoundedCornerShape(8.dp)) + .border(width = 1.dp, color = border, shape = RoundedCornerShape(8.dp)) + .padding(4.dp), + imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet_connect_logo), + contentDescription = ContentDescription.WC_LOGO.description, + + colorFilter = colorFilter + ) +} + +@UiModePreview +@Composable +private fun PreviewWalletConnectLogo() { + MultipleComponentsPreview( + { WalletConnectLogo() }, + { WalletConnectLogo(false) } + ) +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/model/UiState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/model/UiState.kt new file mode 100644 index 000000000..3bc8d9a57 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/model/UiState.kt @@ -0,0 +1,35 @@ +package com.reown.appkit.ui.model + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.togetherWith +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import com.reown.modal.ui.model.UiState +import com.reown.appkit.ui.components.internal.ErrorModalState +import com.reown.appkit.ui.components.internal.LoadingModalState +import kotlinx.coroutines.flow.StateFlow + +@Composable +internal fun UiStateBuilder( + uiStateFlow: StateFlow>, + onError: @Composable (Throwable) -> Unit = { ErrorModalState {} }, + onLoading: @Composable (T?) -> Unit = { LoadingModalState() }, + onSuccess: @Composable (T) -> Unit +) { + val uiState by uiStateFlow.collectAsState() + AnimatedContent( + targetState = uiState, + label = "UiStateBuilder $uiState", + transitionSpec = { fadeIn() + slideInHorizontally { it / 2 } togetherWith fadeOut() } + ) { state -> + when (state) { + is UiState.Error -> onError(state.error) + is UiState.Loading -> onLoading(state.data) + is UiState.Success -> onSuccess(state.data) + } + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/NavUtils.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/NavUtils.kt similarity index 83% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/NavUtils.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/NavUtils.kt index b5de9a164..0f72f9fb9 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/NavUtils.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/NavUtils.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.navigation +package com.reown.appkit.ui.navigation import androidx.navigation.NavBackStackEntry diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Navigator.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Navigator.kt similarity index 95% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Navigator.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Navigator.kt index 0fbee267d..0eb5051e9 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Navigator.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Navigator.kt @@ -1,10 +1,10 @@ -package com.walletconnect.web3.modal.ui.navigation +package com.reown.appkit.ui.navigation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.navigation.NavController -import com.walletconnect.web3.modal.ui.components.internal.snackbar.LocalSnackBarHandler +import com.reown.appkit.ui.components.internal.snackbar.LocalSnackBarHandler import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Route.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Route.kt new file mode 100644 index 000000000..3ece6dd53 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/Route.kt @@ -0,0 +1,24 @@ +package com.reown.appkit.ui.navigation + +//Todo Think about split it into own graphs routes + +enum class Route(val path: String, val title: String? = null) { + //Common + APPKIT("web3_modal"), + CHOOSE_NETWORK("choose_network", "Select network"), + //Connect routes + CONNECT_YOUR_WALLET("connect_your_wallet", "Connect wallet"), + QR_CODE("qr_code", "WalletConnect"), + WHAT_IS_WALLET("what_is_wallet", "What is a wallet?"), + GET_A_WALLET("get_a_wallet", "Get a wallet"), + ALL_WALLETS("all_wallets", "All wallets"), + REDIRECT("redirect"), + + //Session routes + ACCOUNT("account"), + CHANGE_NETWORK("change_network", "Select network"), + CHAIN_SWITCH_REDIRECT("chain_switch_redirect"), + RECENT_TRANSACTION("recent_transaction"), + WHAT_IS_NETWORK("what_is_network", "What is a network?"), + SIWE_FALLBACK("siwe_fallback", "Sign In") +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/account/Navigation.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/account/Navigation.kt new file mode 100644 index 000000000..7d4055170 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/account/Navigation.kt @@ -0,0 +1,34 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.appkit.ui.navigation.account + +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import com.google.accompanist.navigation.animation.composable +import androidx.navigation.navArgument +import com.reown.util.Empty +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.addTitleArg +import com.reown.appkit.ui.routes.account.AccountViewModel +import com.reown.appkit.ui.routes.account.chain_redirect.ChainSwitchRedirectRoute + +private const val CHAIN_ID_KEY = "chainId" +private const val CHAIN_ID_ARG = "{chainId}" + +internal fun Modal.Model.Chain.toChainSwitchPath() = Route.CHAIN_SWITCH_REDIRECT.path + "/${id}&${chainName}" + +internal fun NavGraphBuilder.chainSwitchRoute( + accountViewModel: AccountViewModel +) { + composable( + route = Route.CHAIN_SWITCH_REDIRECT.path + "/" + CHAIN_ID_ARG + addTitleArg(), + arguments = listOf(navArgument(CHAIN_ID_KEY) { type = NavType.StringType } ) + ) { backStackEntry -> + val chainId = backStackEntry.arguments?.getString(CHAIN_ID_KEY, String.Empty) + val chain = AppKit.chains.find { it.id == chainId } + chain?.let { ChainSwitchRedirectRoute(accountViewModel = accountViewModel, chain = it) } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/connection/RedirectNavigation.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/connection/RedirectNavigation.kt new file mode 100644 index 000000000..4ce85f924 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/navigation/connection/RedirectNavigation.kt @@ -0,0 +1,34 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.appkit.ui.navigation.connection + +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.navArgument +import com.google.accompanist.navigation.animation.composable +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.util.Empty +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.addTitleArg +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.routes.connect.redirect.RedirectWalletRoute +import timber.log.Timber + +private const val WALLET_ID_KEY = "walletId" +private const val WALLET_ID_ARG = "{walletId}" + +internal fun Wallet.toRedirectPath() = Route.REDIRECT.path + "/${id}&${name}" + +internal fun NavGraphBuilder.redirectRoute( + connectViewModel: ConnectViewModel +) { + composable( + route = Route.REDIRECT.path + "/" + WALLET_ID_ARG + addTitleArg(), + arguments = listOf(navArgument(WALLET_ID_KEY) { type = NavType.StringType }) + ) { backStackEntry -> + val walletId = backStackEntry.arguments?.getString(WALLET_ID_KEY, String.Empty) + val wallet = connectViewModel.getWallet(walletId) + wallet?.let { RedirectWalletRoute(connectState = connectViewModel, wallet = it) } ?: Timber.e("Invalid wallet id") + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/PreviewData.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/PreviewData.kt similarity index 95% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/PreviewData.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/PreviewData.kt index 05488191c..c84e68574 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/PreviewData.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/PreviewData.kt @@ -1,9 +1,9 @@ -package com.walletconnect.web3.modal.ui.previews +package com.reown.appkit.ui.previews import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.model.AccountData +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.model.AccountData private val metaMask: Wallet get() = Wallet(id = "1", name = "MetaMask", homePage = "", order = "", imageUrl = "", mobileLink = "metamask://", webAppLink = "", playStore = "", linkMode = "").apply { isRecent = true } diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/Previews.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/Previews.kt new file mode 100644 index 000000000..7ce71c4ed --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/previews/Previews.kt @@ -0,0 +1,88 @@ +package com.reown.appkit.ui.previews + +import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.compose.rememberNavController +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.wcKoinApp +import com.reown.appkit.ui.components.internal.root.AppKitRoot +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.root.rememberAppKitRootState +import com.reown.appkit.ui.components.internal.snackbar.rememberSnackBarState +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import com.reown.appkit.ui.theme.AppKitTheme +import org.koin.dsl.module + +@Composable +internal fun AppKitPreview( + title: String? = null, + content: @Composable () -> Unit, +) { + previewKoinDefinitions() + ProvideAppKitThemeComposition { + val scope = rememberCoroutineScope() + val rootState = rememberAppKitRootState(coroutineScope = scope, navController = rememberNavController()) + val snackBarState = rememberSnackBarState(coroutineScope = scope) + + AppKitRoot(rootState = rootState, snackBarState = snackBarState, closeModal = {}, title = title) { + content() + } + } +} + +@Composable +internal fun ComponentPreview( + content: @Composable () -> Unit +) { + previewKoinDefinitions() + ProvideAppKitThemeComposition { + Column(modifier = Modifier.background(AppKitTheme.colors.background.color100)) { + content() + } + } +} + +@Composable +internal fun MultipleComponentsPreview( + vararg content: @Composable () -> Unit +) { + previewKoinDefinitions() + ProvideAppKitThemeComposition { + Column { + content.forEach { + VerticalSpacer(height = 5.dp) + Box(modifier = Modifier.background(AppKitTheme.colors.background.color100)) { it() } + VerticalSpacer(height = 5.dp) + } + } + } +} + +private fun previewKoinDefinitions() { + val modules = listOf( + module { single { ProjectId("fakeId") } } + ) + wcKoinApp.koin.loadModules(modules = modules) +} + +@LightTheme +@DarkTheme +internal annotation class UiModePreview + +@Preview(uiMode = UI_MODE_NIGHT_NO) +internal annotation class LightTheme + +@Preview(uiMode = UI_MODE_NIGHT_YES) +internal annotation class DarkTheme + +@Preview(device = Devices.AUTOMOTIVE_1024p, widthDp = 720, heightDp = 360, uiMode = UI_MODE_NIGHT_YES) +internal annotation class Landscape diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountNavigationGraph.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountNavigationGraph.kt new file mode 100644 index 000000000..e97fd242f --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountNavigationGraph.kt @@ -0,0 +1,48 @@ +package com.reown.appkit.ui.routes.account + +import androidx.compose.runtime.Composable +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import com.reown.appkit.ui.navigation.ConsumeNavigationEventsEffect +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.account.chainSwitchRoute +import com.reown.appkit.ui.routes.account.account.AccountRoute +import com.reown.appkit.ui.routes.account.change_network.ChangeNetworkRoute +import com.reown.appkit.ui.routes.account.what_is_network.WhatIsNetworkRoute +import com.reown.appkit.ui.utils.AnimatedNavGraph +import com.reown.appkit.ui.utils.animatedComposable + +@Composable +internal fun AccountNavGraph( + navController: NavHostController, + closeModal: () -> Unit, + shouldOpenChangeNetwork: Boolean +) { + val startDestination = if (shouldOpenChangeNetwork) Route.CHANGE_NETWORK.path else Route.ACCOUNT.path + val accountViewModel = viewModel() + + ConsumeNavigationEventsEffect( + navController = navController, + navigator = accountViewModel, + closeModal = closeModal + ) + + AnimatedNavGraph( + navController = navController, + startDestination = startDestination + ) { + animatedComposable(route = Route.ACCOUNT.path) { + AccountRoute( + accountViewModel = accountViewModel, + navController = navController + ) + } + animatedComposable(route = Route.CHANGE_NETWORK.path) { + ChangeNetworkRoute(accountViewModel = accountViewModel) + } + animatedComposable(route = Route.WHAT_IS_WALLET.path) { + WhatIsNetworkRoute() + } + chainSwitchRoute(accountViewModel = accountViewModel) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountViewModel.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountViewModel.kt new file mode 100644 index 000000000..f6cde9fd3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/AccountViewModel.kt @@ -0,0 +1,185 @@ +package com.reown.appkit.ui.routes.account + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.modal.ui.model.UiState +import com.reown.appkit.client.Modal +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.client.models.request.SentRequestResult +import com.reown.appkit.domain.model.AccountData +import com.reown.appkit.domain.model.Session +import com.reown.appkit.domain.usecase.GetEthBalanceUseCase +import com.reown.appkit.domain.usecase.GetIdentityUseCase +import com.reown.appkit.domain.usecase.ObserveSelectedChainUseCase +import com.reown.appkit.domain.usecase.ObserveSessionUseCase +import com.reown.appkit.domain.usecase.SaveChainSelectionUseCase +import com.reown.appkit.domain.usecase.SaveSessionUseCase +import com.reown.appkit.engine.AppKitEngine +import com.reown.appkit.engine.coinbase.CoinbaseResult +import com.reown.appkit.ui.navigation.Navigator +import com.reown.appkit.ui.navigation.NavigatorImpl +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.account.toChainSwitchPath +import com.reown.appkit.utils.EthUtils +import com.reown.appkit.utils.createAddEthChainParams +import com.reown.appkit.utils.createSwitchChainParams +import com.reown.appkit.utils.getChains +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +internal class AccountViewModel : ViewModel(), Navigator by NavigatorImpl() { + private val logger: Logger = wcKoinApp.koin.get() + + private val saveChainSelectionUseCase: SaveChainSelectionUseCase = wcKoinApp.koin.get() + private val saveSessionUseCase: SaveSessionUseCase = wcKoinApp.koin.get() + private val observeSessionUseCase: ObserveSessionUseCase = wcKoinApp.koin.get() + private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() + private val getIdentityUseCase: GetIdentityUseCase = wcKoinApp.koin.get() + private val getEthBalanceUseCase: GetEthBalanceUseCase = wcKoinApp.koin.get() + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() + private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() + private val activeSessionFlow = observeSessionUseCase() + + private val accountDataFlow = activeSessionFlow + .map { + if (appKitEngine.getAccount() != null) { + it + } else { + null + } + } + .map { activeSession -> + if (activeSession != null) { + val chains = activeSession.getChains() + val identity = getIdentityUseCase(activeSession.address, activeSession.chain) + accountData = AccountData( + address = activeSession.address, chains = chains, identity = identity + ) + UiState.Success(accountData) + } else { + UiState.Error(Throwable("Active session not found")) + } + }.catch { + showError(it.localizedMessage) + logger.error(it) + emit(UiState.Error(it)) + } + + lateinit var accountData: AccountData + + val accountState = accountDataFlow.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = UiState.Loading()) + + val selectedChain = observeSelectedChainUseCase().map { appKitEngine.getSelectedChainOrFirst() } + + val balanceState = combine(activeSessionFlow, selectedChain) { session, selectedChain -> + if (session != null && selectedChain.rpcUrl != null) { + return@combine getEthBalanceUseCase(selectedChain.token, selectedChain.rpcUrl, session.address) + } else { + null + } + }.flowOn(Dispatchers.IO).catch { logger.error(it) }.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = null) + + fun disconnect() { + appKitEngine.disconnect( + onSuccess = { closeModal() }, + onError = { + showError(it.localizedMessage) + logger.error(it) + } + ) + } + + fun changeActiveChain(chain: Modal.Model.Chain) = viewModelScope.launch { + if (accountData.chains.contains(chain)) { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SWITCH_NETWORK, Properties(network = chain.id))) + saveChainSelectionUseCase(chain.id) + popBackStack() + } else { + navigateTo(chain.toChainSwitchPath()) + } + } + + suspend fun updatedSessionAfterChainSwitch(updatedSession: Session) { + if (updatedSession.getChains().any { it.id == updatedSession.chain }) { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SWITCH_NETWORK, Properties(network = updatedSession.chain))) + saveSessionUseCase(updatedSession) + popBackStack(path = Route.CHANGE_NETWORK.path, inclusive = true) + } + } + + fun switchChain(to: Modal.Model.Chain, onReject: () -> Unit) { + val onError: (String?) -> Unit = { showError(it ?: "Something went wrong") } + val isChainApproved = accountData.chains.contains(to) + val onSuccess: (SentRequestResult) -> Unit = { it.handleRequestResult(to, onError, onReject) } + if (!isChainApproved && to.optionalMethods.contains(EthUtils.walletAddEthChain)) { + addEthChain(to, onSuccess, onError) + } else { + switchEthChain(to, onSuccess, onError) + } + } + + private fun SentRequestResult.handleRequestResult( + to: Modal.Model.Chain, + onError: (String?) -> Unit, + onReject: () -> Unit + ) { + when (this) { + is SentRequestResult.Coinbase -> this.results.firstOrNull()?.let { + when (it) { + is CoinbaseResult.Error -> { + onError(it.message) + onReject() + } + + is CoinbaseResult.Result -> { + viewModelScope.launch { + updatedSessionAfterChainSwitch(Session.Coinbase(to.id, accountData.address)) + logger.log("Successful request: ${it.value}") + } + } + } + } + + is SentRequestResult.WalletConnect -> logger.log("Successful request: ${this.requestId}") + } + } + + private fun switchEthChain( + to: Modal.Model.Chain, + onSuccess: (SentRequestResult) -> Unit, + onError: (String?) -> Unit + ) { + appKitEngine.request( + Request(method = EthUtils.walletSwitchEthChain, params = createSwitchChainParams(to)), + onSuccess + ) { onError(it.message) } + } + + private fun addEthChain( + to: Modal.Model.Chain, onSuccess: (SentRequestResult) -> Unit, onError: (String?) -> Unit + ) { + appKitEngine.request( + Request(method = EthUtils.walletAddEthChain, params = createAddEthChainParams(to)), + onSuccess + ) { onError(it.message) } + } + + fun getSelectedChainOrFirst() = appKitEngine.getSelectedChainOrFirst() + + fun navigateToHelp() { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORK_HELP)) + navigateTo(Route.WHAT_IS_WALLET.path) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/account/AccountRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/account/AccountRoute.kt new file mode 100644 index 000000000..cab51fd0c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/account/AccountRoute.kt @@ -0,0 +1,145 @@ +package com.reown.appkit.ui.routes.account.account + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.model.AccountData +import com.reown.appkit.domain.model.Balance +import com.reown.appkit.ui.components.internal.commons.CloseIcon +import com.reown.appkit.ui.components.internal.commons.CompassIcon +import com.reown.appkit.ui.components.internal.commons.DisconnectIcon +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.account.AccountName +import com.reown.appkit.ui.components.internal.commons.account.AccountImage +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ChipButton +import com.reown.appkit.ui.components.internal.commons.entry.AccountEntry +import com.reown.appkit.ui.components.internal.commons.entry.AccountEntryState +import com.reown.appkit.ui.components.internal.commons.network.CircleNetworkImage +import com.reown.appkit.ui.model.UiStateBuilder +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.ethereumChain +import com.reown.appkit.ui.routes.account.AccountViewModel +import com.reown.appkit.ui.previews.accountDataPreview +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.getImageData + +@Composable +internal fun AccountRoute( + navController: NavController, + accountViewModel: AccountViewModel +) { + val uriHandler = LocalUriHandler.current + val selectedChain by accountViewModel.selectedChain.collectAsState(initial = accountViewModel.getSelectedChainOrFirst()) + val balance by accountViewModel.balanceState.collectAsState() + + Box(modifier = Modifier.fillMaxWidth()) { + UiStateBuilder(uiStateFlow = accountViewModel.accountState) { data -> + AccountScreen( + accountData = data, + selectedChain = selectedChain, + balance = balance, + onBlockExplorerClick = { url -> uriHandler.openUri(url) }, + onChangeNetworkClick = { navController.navigate(Route.CHANGE_NETWORK.path) }, + onDisconnectClick = { accountViewModel.disconnect() } + ) + } + CloseIcon( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(18.dp), + onClick = { accountViewModel.closeModal() } + ) + } +} + +@Composable +private fun AccountScreen( + accountData: AccountData, + selectedChain: Modal.Model.Chain, + balance: Balance?, + onBlockExplorerClick: (String) -> Unit, + onChangeNetworkClick: () -> Unit, + onDisconnectClick: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding( + top = 32.dp, bottom = 16.dp, start = 12.dp, end = 12.dp + ), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AccountImage(address = accountData.address, avatarUrl = accountData.identity?.avatar) + VerticalSpacer(height = 20.dp) + AccountName(accountData) + balance?.let { balance -> + VerticalSpacer(height = 4.dp) + Text( + text = balance.valueWithSymbol, + style = AppKitTheme.typo.paragraph400.copy(AppKitTheme.colors.foreground.color200) + ) + } + selectedChain.blockExplorerUrl?.let { url -> + VerticalSpacer(height = 12.dp) + ChipButton( + text = "Block Explorer", + startIcon = { CompassIcon() }, + endIcon = { ExternalIcon(it) }, + style = ButtonStyle.SHADE, + size = ButtonSize.S, + onClick = { onBlockExplorerClick("$url/address/${accountData.address}") } + ) + } + VerticalSpacer(height = 20.dp) + AccountEntry( + startIcon = { CircleNetworkImage(selectedChain.getImageData()) }, + onClick = onChangeNetworkClick, + state = AccountEntryState.NEXT + ) { + Text(text = selectedChain.chainName, style = AppKitTheme.typo.paragraph500.copy(color = it.textColor)) + } + VerticalSpacer(height = 8.dp) + AccountEntry( + startIcon = { DisconnectIcon() }, + onClick = onDisconnectClick + ) { + Text(text = "Disconnect", style = AppKitTheme.typo.paragraph500.copy(color = it.textColor)) + } + } +} + +@UiModePreview +@Composable +private fun PreviewAccountScreen() { + AppKitPreview { + AccountScreen(accountDataPreview, ethereumChain,null, {}, {}, {}) + } +} + +@UiModePreview +@Composable +private fun PreviewAccountScreenWithBalance() { + AppKitPreview { + AccountScreen(accountDataPreview, ethereumChain, Balance(ethereumChain.token, "0000000"), {}, {}, {}) + } +} + diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainRedirectState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainRedirectState.kt new file mode 100644 index 000000000..9fd9c8df3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainRedirectState.kt @@ -0,0 +1,7 @@ +package com.reown.appkit.ui.routes.account.chain_redirect + +internal sealed class ChainRedirectState { + object Loading : ChainRedirectState() + + object Declined: ChainRedirectState() +} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt index ad984bec5..e1a14d3b4 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/chain_redirect/ChainSwitchRedirect.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.routes.account.chain_redirect +package com.reown.appkit.ui.routes.account.chain_redirect import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility @@ -20,20 +20,20 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.ui.components.internal.commons.DeclinedIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingHexagonBorder -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.button.TryAgainButton -import com.walletconnect.web3.modal.ui.components.internal.commons.network.HexagonNetworkImage -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.testChains -import com.walletconnect.web3.modal.ui.routes.account.AccountViewModel -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.getImageData -import com.walletconnect.web3.modal.utils.toSession +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.ui.components.internal.commons.DeclinedIcon +import com.reown.appkit.ui.components.internal.commons.LoadingHexagonBorder +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.button.TryAgainButton +import com.reown.appkit.ui.components.internal.commons.network.HexagonNetworkImage +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.testChains +import com.reown.appkit.ui.routes.account.AccountViewModel +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.utils.getImageData +import com.reown.appkit.utils.toSession import kotlinx.coroutines.launch @Composable @@ -52,7 +52,7 @@ internal fun ChainSwitchRedirectRoute( } LaunchedEffect(Unit) { - Web3ModalDelegate.wcEventModels.collect { + AppKitDelegate.wcEventModels.collect { when (it) { is Modal.Model.UpdatedSession -> accountViewModel.updatedSessionAfterChainSwitch(it.toSession(chain)) is Modal.Model.SessionRequestResponse -> if (it.result is Modal.Model.JsonRpcResponse.JsonRpcError) { onReject() } @@ -100,11 +100,11 @@ internal fun ChainSwitchRedirectScreen( private fun ChainSwitchInfo(redirectState: ChainRedirectState) { AnimatedContent(targetState = redirectState, label = "Chain switch info") { state -> Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text(text = state.toTitle(), style = Web3ModalTheme.typo.paragraph500) + Text(text = state.toTitle(), style = AppKitTheme.typo.paragraph500) VerticalSpacer(height = 8.dp) Text( text = state.toInformation(), - style = Web3ModalTheme.typo.small400.copy(Web3ModalTheme.colors.foreground.color200, textAlign = TextAlign.Center) + style = AppKitTheme.typo.small400.copy(AppKitTheme.colors.foreground.color200, textAlign = TextAlign.Center) ) } } @@ -150,7 +150,7 @@ private fun ChainNetworkImageWrapper( Box( modifier = Modifier .align(Alignment.BottomEnd) - .background(Web3ModalTheme.colors.background.color100, shape = CircleShape) + .background(AppKitTheme.colors.background.color100, shape = CircleShape) .padding(2.dp) ) { DeclinedIcon() @@ -171,7 +171,7 @@ private fun ChainNetworkImageWrapper( @UiModePreview private fun ChainSwitchRedirectScreenWithLoadingStatePreview() { val chain = testChains.first() - Web3ModalPreview(title = chain.chainName) { + AppKitPreview(title = chain.chainName) { ChainSwitchRedirectScreen(chain, ChainRedirectState.Loading, {}) } } @@ -180,7 +180,7 @@ private fun ChainSwitchRedirectScreenWithLoadingStatePreview() { @UiModePreview private fun ChainSwitchRedirectScreenWithDeclinedStatePreview() { val chain = testChains.first() - Web3ModalPreview(title = chain.chainName) { + AppKitPreview(title = chain.chainName) { ChainSwitchRedirectScreen(chain, ChainRedirectState.Declined, {}) } } diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/change_network/ChangeNetworkRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/change_network/ChangeNetworkRoute.kt new file mode 100644 index 000000000..34bff4bf2 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/change_network/ChangeNetworkRoute.kt @@ -0,0 +1,99 @@ +package com.reown.appkit.ui.routes.account.change_network + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.itemsIndexed +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.ui.components.internal.commons.NetworkBottomSection +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.network.ChainNetworkItem +import com.reown.appkit.ui.model.UiStateBuilder +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.ethereumChain +import com.reown.appkit.ui.previews.testChains +import com.reown.appkit.ui.routes.account.AccountViewModel +import com.reown.appkit.utils.getChainNetworkImageUrl + +@Composable +internal fun ChangeNetworkRoute( + accountViewModel: AccountViewModel +) { + val selectedChain by accountViewModel.selectedChain.collectAsState(initial = accountViewModel.getSelectedChainOrFirst()) + + UiStateBuilder(uiStateFlow = accountViewModel.accountState) { + ChangeNetworkScreen( + chains = AppKit.chains, + selectedChain = selectedChain, + onChainItemClick = { accountViewModel.changeActiveChain(it) }, + onWhatIsWalletClick = { accountViewModel.navigateToHelp() } + ) + } +} + +@Composable +private fun ChangeNetworkScreen( + chains: List, + selectedChain: Modal.Model.Chain, + onChainItemClick: (Modal.Model.Chain) -> Unit, + onWhatIsWalletClick: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + VerticalSpacer(height = 8.dp) + ChainNetworkGrid( + chains = chains, + selectedChain = selectedChain, + onItemClick = { onChainItemClick(it) } + ) + NetworkBottomSection(onWhatIsWalletClick) + } +} + +@Composable +private fun ChainNetworkGrid( + chains: List, + selectedChain: Modal.Model.Chain, + onItemClick: (Modal.Model.Chain) -> Unit +) { + LazyVerticalGrid( + columns = GridCells.FixedSize(82.dp), + modifier = Modifier.padding(horizontal = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalArrangement = Arrangement.Center, + content = { + itemsIndexed(chains) { _, item -> + ChainNetworkItem( + isSelected = item.id == selectedChain.id, + networkName = item.chainName, + image = item.chainImage ?: getChainNetworkImageUrl(item.chainReference) + ) { + onItemClick(item) + } + } + } + ) +} + +@Composable +@UiModePreview +private fun ChangeNetworkPreview() { + AppKitPreview("Change Network") { + ChangeNetworkScreen(testChains, ethereumChain, {}, {}) + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt similarity index 90% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt index f9b7d17db..ebe4be785 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/siwe_fallback/SIWEFallbackRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.routes.account.siwe_fallback +package com.reown.appkit.ui.routes.account.siwe_fallback import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background @@ -32,10 +32,10 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletImageWithLoader -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.WalletImageWithLoader +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun SIWEFallbackRoute( @@ -71,20 +71,20 @@ private fun SIWEFallback( VerticalSpacer(4.dp) Text( text = connectViewModel.wallet?.name ?: "", - style = Web3ModalTheme.typo.paragraph400, + style = AppKitTheme.typo.paragraph400, textAlign = TextAlign.Center, ) VerticalSpacer(20.dp) Spacer(modifier = Modifier.height(20.dp)) Text( - text = "Web3Modal needs to connect to your wallet", - style = Web3ModalTheme.typo.paragraph400, + text = "AppKit needs to connect to your wallet", + style = AppKitTheme.typo.paragraph400, textAlign = TextAlign.Center, ) Spacer(modifier = Modifier.height(8.dp)) Text( text = "Sign this message to prove you own this wallet and proceed.\\n Cancelling will disconnect you.", - style = Web3ModalTheme.typo.small400.copy(Web3ModalTheme.colors.foreground.color200), + style = AppKitTheme.typo.small400.copy(AppKitTheme.colors.foreground.color200), textAlign = TextAlign.Center, ) VerticalSpacer(20.dp) @@ -114,7 +114,7 @@ fun Buttons( buttonColor = Color(0xFFFFFFFF), loaderColor = Color(0xFF000000), modifier = Modifier - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass05, shape = RoundedCornerShape(20.dp)) + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass05, shape = RoundedCornerShape(20.dp)) .weight(1f) .height(46.dp) .clickable { onCancel() }, diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt new file mode 100644 index 000000000..1081da838 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt @@ -0,0 +1,74 @@ +package com.reown.appkit.ui.routes.account.what_is_network + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.HelpSection +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun WhatIsNetworkRoute() { + val uriHandler = LocalUriHandler.current + + WhatIsNetwork { uriHandler.openUri("https://ethereum.org/en/developers/docs/networks/") } +} + +@Composable +private fun WhatIsNetwork( + onLearnMoreClick: () -> Unit +) { + Column( + modifier = Modifier + .padding(horizontal = 20.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + VerticalSpacer(20.dp) + HelpSection( + title = "The system's nuts and bolts", + body = "A network is what brings the blockchain to life, as this technical infrastructure allows apps to access the ledger and smart contract services.", + assets = listOf(R.drawable.network, R.drawable.layers, R.drawable.system) + ) + VerticalSpacer(24.dp) + HelpSection( + title = "Designed for different uses", + body = "Each network is designed differently, and may therefore suit certain apps and experiences.", + assets = listOf(R.drawable.noun, R.drawable.defi_alt, R.drawable.dao) + ) + VerticalSpacer(height = 20.dp) + ImageButton( + text = "Learn more", + image = { ExternalIcon(AppKitTheme.colors.inverse100) }, + style = ButtonStyle.MAIN, + size = ButtonSize.S, + paddingValues = PaddingValues(start = 8.dp, top = 6.dp, end = 12.dp, 6.dp), + onClick = { onLearnMoreClick() } + ) + Spacer(modifier = Modifier.height(30.dp)) + } +} + +@UiModePreview +@Composable +private fun WhatIsNetworkPreview() { + AppKitPreview { + WhatIsNetworkRoute() + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/common/WhatIsNetwork.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/common/WhatIsNetwork.kt new file mode 100644 index 000000000..093d1f185 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/common/WhatIsNetwork.kt @@ -0,0 +1,69 @@ +package com.reown.appkit.ui.routes.common + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.HelpSection +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun WhatIsNetworkRoute() { + val uriHandler = LocalUriHandler.current + + WhatIsNetwork { + uriHandler.openUri("https://ethereum.org/en/developers/docs/networks/") + } +} + +@Composable +private fun WhatIsNetwork( + onLearnMoreClick: () -> Unit +) { + Column( + modifier = Modifier.padding(horizontal = 20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(20.dp)) + HelpSection( + title = "The system’s nuts and bolts", + body = "A network is what brings the blockchain to life, as this technical infrastructure allows apps to access the ledger and smart contract services.", + assets = listOf(R.drawable.network, R.drawable.layers, R.drawable.system) + ) + Spacer(modifier = Modifier.height(4.dp)) + HelpSection( + title = "Designed for different uses", + body = "Each network is designed differently, and may therefore suit certain apps and experiences.", + assets = listOf(R.drawable.noun, R.drawable.defi_alt, R.drawable.dao) + ) + Spacer(modifier = Modifier.height(10.dp)) + ImageButton( + text = "Learn more", + image = { ExternalIcon(AppKitTheme.colors.accent100) }, + style = ButtonStyle.MAIN, + size = ButtonSize.S, + onClick = onLearnMoreClick + ) + Spacer(modifier = Modifier.height(30.dp)) + } +} + +@UiModePreview +@Composable +private fun WhatIsNetworkPreview() { + AppKitPreview(title = "What is a network?") { + WhatIsNetworkRoute() + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectNavigationGraph.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectNavigationGraph.kt new file mode 100644 index 000000000..7b8dd3749 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectNavigationGraph.kt @@ -0,0 +1,69 @@ +package com.reown.appkit.ui.routes.connect + +import androidx.compose.runtime.Composable +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import com.reown.appkit.ui.navigation.ConsumeNavigationEventsEffect +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.connection.redirectRoute +import com.reown.appkit.ui.routes.account.siwe_fallback.SIWEFallbackRoute +import com.reown.appkit.ui.routes.common.WhatIsNetworkRoute +import com.reown.appkit.ui.routes.connect.all_wallets.AllWalletsRoute +import com.reown.appkit.ui.routes.connect.choose_network.ChooseNetworkRoute +import com.reown.appkit.ui.routes.connect.connect_wallet.ConnectWalletRoute +import com.reown.appkit.ui.routes.connect.get_wallet.GetAWalletRoute +import com.reown.appkit.ui.routes.connect.scan_code.ScanQRCodeRoute +import com.reown.appkit.ui.routes.connect.what_is_wallet.WhatIsWallet +import com.reown.appkit.ui.utils.AnimatedNavGraph +import com.reown.appkit.ui.utils.animatedComposable + +@Composable +internal fun ConnectionNavGraph( + navController: NavHostController, + closeModal: () -> Unit, + shouldOpenChooseNetwork: Boolean +) { + val connectViewModel = viewModel() + val startDestination = if (shouldOpenChooseNetwork) { + Route.CHOOSE_NETWORK.path + } else { + Route.CONNECT_YOUR_WALLET.path + } + + ConsumeNavigationEventsEffect( + navController = navController, + navigator = connectViewModel, + closeModal = closeModal + ) + + AnimatedNavGraph( + navController = navController, + startDestination = startDestination + ) { + animatedComposable(route = Route.SIWE_FALLBACK.path) { + SIWEFallbackRoute(connectViewModel = connectViewModel) + } + animatedComposable(route = Route.CONNECT_YOUR_WALLET.path) { + ConnectWalletRoute(connectViewModel = connectViewModel) + } + animatedComposable(route = Route.QR_CODE.path) { + ScanQRCodeRoute(connectViewModel = connectViewModel) + } + animatedComposable(route = Route.WHAT_IS_WALLET.path) { + WhatIsWallet(navController = navController) + } + animatedComposable(Route.GET_A_WALLET.path) { + GetAWalletRoute(wallets = connectViewModel.getNotInstalledWallets()) + } + animatedComposable(Route.ALL_WALLETS.path) { + AllWalletsRoute(connectViewModel = connectViewModel) + } + redirectRoute(connectViewModel) + animatedComposable(Route.CHOOSE_NETWORK.path) { + ChooseNetworkRoute(connectViewModel = connectViewModel) + } + animatedComposable(Route.WHAT_IS_NETWORK.path) { + WhatIsNetworkRoute() + } + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectViewModel.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectViewModel.kt new file mode 100644 index 000000000..2de1b93c1 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ConnectViewModel.kt @@ -0,0 +1,242 @@ +package com.reown.appkit.ui.routes.connect + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.ConnectionMethod +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.modal.ui.model.LoadingState +import com.reown.modal.ui.model.UiState +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.client.models.request.SentRequestResult +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.domain.usecase.ObserveSelectedChainUseCase +import com.reown.appkit.domain.usecase.SaveChainSelectionUseCase +import com.reown.appkit.domain.usecase.SaveRecentWalletUseCase +import com.reown.appkit.engine.AppKitEngine +import com.reown.appkit.ui.navigation.Navigator +import com.reown.appkit.ui.navigation.NavigatorImpl +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.navigation.connection.toRedirectPath +import com.reown.appkit.utils.getSelectedChain +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +internal class ConnectViewModel : ViewModel(), Navigator by NavigatorImpl(), ParingController by PairingControllerImpl() { + private val logger: Logger = wcKoinApp.koin.get() + private val walletsDataStore = WalletDataSource { showError(it) } + private val saveRecentWalletUseCase: SaveRecentWalletUseCase = wcKoinApp.koin.get() + private val saveChainSelectionUseCase: SaveChainSelectionUseCase = wcKoinApp.koin.get() + private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() + private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() + private var sessionParams = getSessionParamsSelectedChain(AppKit.selectedChain?.id) + val selectedChain = observeSelectedChainUseCase().map { savedChainId -> + AppKit.chains.find { it.id == savedChainId } ?: appKitEngine.getSelectedChainOrFirst() + } + private var _isConfirmLoading: MutableStateFlow = MutableStateFlow(false) + val isConfirmLoading get() = _isConfirmLoading.asStateFlow() + private var _isCancelLoading: MutableStateFlow = MutableStateFlow(false) + val isCancelLoading get() = _isCancelLoading.asStateFlow() + var wallet: Wallet? = null + + val walletsState: StateFlow = walletsDataStore.searchWalletsState.stateIn(viewModelScope, SharingStarted.Lazily, WalletsData.empty()) + val uiState: StateFlow>> = walletsDataStore.walletState.map { pagingData -> + when { + pagingData.error != null -> UiState.Error(pagingData.error) + pagingData.loadingState == LoadingState.REFRESH -> UiState.Loading() + else -> UiState.Success(pagingData.wallets) + } + }.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = UiState.Loading()) + + val searchPhrase + get() = walletsDataStore.searchPhrase + + init { + AppKitDelegate + .wcEventModels + .filterIsInstance() + .onEach { + _isConfirmLoading.value = false + showError(it.message) + disconnect() + }.launchIn(viewModelScope) + + fetchInitialWallets() + } + + fun disconnect() { + _isCancelLoading.value = true + appKitEngine.disconnect( + onSuccess = { + _isCancelLoading.value = false + closeModal() + }, + onError = { + _isCancelLoading.value = false + showError(it.localizedMessage) + logger.error(it) + } + ) + } + + fun sendSIWEOverPersonalSign() { + _isConfirmLoading.value = true + appKitEngine.shouldDisconnect = false + val account = appKitEngine.getAccount() ?: throw IllegalStateException("Account is null") + val issuer = "did:pkh:${account.chain.id}:${account.address}" + val siweMessage = appKitEngine.formatSIWEMessage(AppKit.authPayloadParams!!, issuer) + val msg = siweMessage.encodeToByteArray().joinToString(separator = "", prefix = "0x") { eachByte -> "%02x".format(eachByte) } + val body = "[\"$msg\", \"${account.address}\"]" + appKitEngine.request( + request = Request("personal_sign", body), + onSuccess = { sendRequest -> + logger.log("SIWE sent successfully") + appKitEngine.siweRequestIdWithMessage = Pair((sendRequest as SentRequestResult.WalletConnect).requestId, siweMessage) + }, + onError = { + if (it !is AppKitEngine.RedirectMissingThrowable) { + appKitEngine.shouldDisconnect = true + } + + _isConfirmLoading.value = false + showError(it.message) + }, + ) + } + + fun fetchInitialWallets() { + viewModelScope.launch { walletsDataStore.fetchInitialWallets() } + } + + fun navigateToHelp() { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORK_HELP)) + navigateTo(Route.WHAT_IS_WALLET.path) + } + + fun navigateToScanQRCode() { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SELECT_WALLET, Properties(name = "WalletConnect", platform = ConnectionMethod.QR_CODE))) + connectWalletConnect(name = "WalletConnect", method = ConnectionMethod.QR_CODE, linkMode = null) { navigateTo(Route.QR_CODE.path) } + } + + fun navigateToRedirectRoute(wallet: Wallet) { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SELECT_WALLET, Properties(name = wallet.name, platform = wallet.toConnectionType()))) + saveRecentWalletUseCase(wallet.id) + walletsDataStore.updateRecentWallet(wallet.id) + navigateTo(wallet.toRedirectPath()) + } + + fun navigateToConnectWallet(chain: Modal.Model.Chain) { + viewModelScope.launch { saveChainSelectionUseCase(chain.id) } + sessionParams = getSessionParamsSelectedChain(chain.id) + navigateTo(Route.CONNECT_YOUR_WALLET.path) + } + + fun navigateToAllWallets() { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_ALL_WALLETS)) + clearSearch() + navigateTo(Route.ALL_WALLETS.path) + } + + fun connectWalletConnect(name: String, method: String, linkMode: String?, onSuccess: (String) -> Unit) { + if (AppKit.authPayloadParams != null) { + authenticate( + name, method, + walletAppLink = linkMode, + authParams = if (AppKit.selectedChain != null) AppKit.authPayloadParams!!.copy(chains = listOf(AppKit.selectedChain!!.id)) else AppKit.authPayloadParams!!, + onSuccess = { onSuccess(it) }, + onError = { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = it.message ?: "Relay error while connecting"))) + showError(it.localizedMessage) + logger.error(it) + } + ) + } else { + connect( + name, method, + sessionParams = sessionParams, + onSuccess = onSuccess, + onError = { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = it.message ?: "Relay error while connecting"))) + showError(it.localizedMessage) + logger.error(it) + } + ) + } + } + + fun connectCoinbase(onSuccess: () -> Unit = {}) { + appKitEngine.connectCoinbase( + onSuccess = onSuccess, + onError = { + showError(it.localizedMessage) + logger.error(it) + } + ) + } + + fun fetchMoreWallets() { + viewModelScope.launch { walletsDataStore.fetchMoreWallets() } + } + + fun search(searchPhrase: String) { + viewModelScope.launch { walletsDataStore.searchWallet(searchPhrase) } + } + + fun clearSearch() = walletsDataStore.clearSearch() + + fun getWallet(walletId: String?) = walletsDataStore.getWallet(walletId).also { wallet = it } + + fun getNotInstalledWallets() = walletsDataStore.wallets.filterNot { it.isWalletInstalled } + + fun getWalletsTotalCount() = walletsDataStore.totalWalletsCount + + private fun Wallet.toConnectionType(): String { + if (isWalletInstalled) ConnectionMethod.MOBILE + + return when { + hasMobileWallet && hasWebApp -> ConnectionMethod.UNDEFINED + hasMobileWallet -> ConnectionMethod.MOBILE + hasWebApp -> ConnectionMethod.WEB + else -> ConnectionMethod.UNDEFINED + } + } + + private fun getSessionParamsSelectedChain(chainId: String?) = with(AppKit.chains) { + val selectedChain = getSelectedChain(chainId) + Modal.Params.SessionParams( + requiredNamespaces = mapOf( + selectedChain.chainNamespace to Modal.Model.Namespace.Proposal( + chains = listOf(selectedChain.id), + methods = selectedChain.requiredMethods, + events = selectedChain.events + ) + ), + optionalNamespaces = filter { it.id != selectedChain.id }.toOptionalNamespaces() + ) + } + + private fun List.toOptionalNamespaces() = groupBy { it.chainNamespace } + .map { (key: String, value: List) -> + key to Modal.Model.Namespace.Proposal( + chains = value.map { it.id }, + methods = value.flatMap { it.requiredMethods + it.optionalMethods }.distinct(), + events = value.flatMap { it.events }.distinct() + ) + }.toMap() +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ParingController.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ParingController.kt similarity index 83% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ParingController.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ParingController.kt index 661361db3..d0974a761 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ParingController.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/ParingController.kt @@ -1,11 +1,11 @@ -package com.walletconnect.web3.modal.ui.routes.connect +package com.reown.appkit.ui.routes.connect -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.toModel -import com.walletconnect.web3.modal.engine.Web3ModalEngine +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.internal.common.wcKoinApp +import com.reown.appkit.client.Modal +import com.reown.appkit.client.toModel +import com.reown.appkit.engine.AppKitEngine internal interface ParingController { @@ -29,7 +29,7 @@ internal interface ParingController { internal class PairingControllerImpl : ParingController { - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() private var _pairing: Core.Model.Pairing? = null @@ -50,7 +50,7 @@ internal class PairingControllerImpl : ParingController { sessionParams.properties, pairing ) - web3ModalEngine.connectWC( + appKitEngine.connectWC( name = name, method = method, connect = connectParams, onSuccess = onSuccess, @@ -71,7 +71,7 @@ internal class PairingControllerImpl : ParingController { ) { try { generateAuthenticatedPairing() - web3ModalEngine.authenticate( + appKitEngine.authenticate( name = name, method = method, authenticate = authParams.toModel(pairing.topic), diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WalletDataSource.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/WalletDataSource.kt similarity index 83% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WalletDataSource.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/WalletDataSource.kt index d85580708..a8fe4b302 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WalletDataSource.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/WalletDataSource.kt @@ -1,15 +1,15 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.domain.usecase.GetRecentWalletUseCase -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import com.walletconnect.web3.modal.engine.coinbase.COINBASE_WALLET_ID -import com.walletconnect.modal.ui.model.LoadingState +package com.reown.appkit.ui.routes.connect + +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface +import com.reown.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface +import com.reown.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface +import com.reown.android.internal.common.wcKoinApp +import com.reown.util.Empty +import com.reown.appkit.domain.usecase.GetRecentWalletUseCase +import com.reown.appkit.engine.AppKitEngine +import com.reown.appkit.engine.coinbase.COINBASE_WALLET_ID +import com.reown.modal.ui.model.LoadingState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -33,7 +33,7 @@ internal class WalletDataSource( private val getWalletsAppDataUseCase: GetInstalledWalletsIdsUseCaseInterface = wcKoinApp.koin.get() private val getRecentWalletUseCase: GetRecentWalletUseCase = wcKoinApp.koin.get() private val getSampleWalletsUseCase: GetSampleWalletsUseCaseInterface = wcKoinApp.koin.get() - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() + private val appKitEngine: AppKitEngine = wcKoinApp.koin.get() private var installedWalletsIds: List = listOf() @@ -48,7 +48,7 @@ internal class WalletDataSource( val wallets: List get() = walletsListingData.wallets - private fun getPriorityWallets() = (getRecentWalletUseCase()?.let { listOf(it) } ?: listOf()) + installedWalletsIds + web3ModalEngine.recommendedWalletsIds + private fun getPriorityWallets() = (getRecentWalletUseCase()?.let { listOf(it) } ?: listOf()) + installedWalletsIds + appKitEngine.recommendedWalletsIds private val searchState: MutableStateFlow = MutableStateFlow(WalletsData.empty()) val walletState: MutableStateFlow = MutableStateFlow(WalletsData.empty()) @@ -62,7 +62,7 @@ internal class WalletDataSource( fetchWalletsAppData() val installedWallets = fetchInstalledAndRecommendedWallets() val samples = getSampleWalletsUseCase() - val walletsListing = getWalletsUseCase(sdkType = W3M_SDK, page = 1, excludeIds = getPriorityWallets() + web3ModalEngine.excludedWalletsIds) + val walletsListing = getWalletsUseCase(sdkType = W3M_SDK, page = 1, excludeIds = getPriorityWallets() + appKitEngine.excludedWalletsIds) walletsListingData = ListingData( page = 1, totalCount = walletsListing.totalCount + samples.size, @@ -81,7 +81,7 @@ internal class WalletDataSource( private suspend fun fetchWalletsAppData() { val walletsIds = getWalletsAppDataUseCase(sdkType = W3M_SDK).toMutableList() - if (!web3ModalEngine.coinbaseIsEnabled()) { + if (!appKitEngine.coinbaseIsEnabled()) { walletsIds.remove(COINBASE_WALLET_ID) } installedWalletsIds = walletsIds @@ -91,7 +91,7 @@ internal class WalletDataSource( sdkType = W3M_SDK, page = 1, includes = getPriorityWallets(), - excludeIds = web3ModalEngine.excludedWalletsIds + excludeIds = appKitEngine.excludedWalletsIds ) suspend fun fetchMoreWallets() { @@ -99,7 +99,7 @@ internal class WalletDataSource( if (walletsListingData.wallets.size < walletsListingData.totalCount) { try { walletState.value = WalletsData.append(walletsListingData.wallets) - val response = getWalletsUseCase(sdkType = W3M_SDK, page = walletsListingData.page + 1, excludeIds = getPriorityWallets() + web3ModalEngine.excludedWalletsIds) + val response = getWalletsUseCase(sdkType = W3M_SDK, page = walletsListingData.page + 1, excludeIds = getPriorityWallets() + appKitEngine.excludedWalletsIds) walletsListingData.addNextPage(response.wallets) walletState.value = WalletsData.submit(walletsListingData.wallets) } catch (exception: Exception) { @@ -125,7 +125,7 @@ internal class WalletDataSource( if (walletsListingData.wallets.size < walletsListingData.totalCount) { try { searchState.value = WalletsData.refresh() - val searchResponse = getWalletsUseCase(sdkType = W3M_SDK, search = searchPhrase, page = 1, excludeIds = web3ModalEngine.excludedWalletsIds) + val searchResponse = getWalletsUseCase(sdkType = W3M_SDK, search = searchPhrase, page = 1, excludeIds = appKitEngine.excludedWalletsIds) searchListingData = ListingData(page = 1, totalCount = searchResponse.totalCount, wallets = searchResponse.wallets) searchState.value = WalletsData.submit(wallets = searchListingData.wallets) } catch (exception: Exception) { @@ -143,7 +143,7 @@ internal class WalletDataSource( if (searchListingData.wallets.size < searchListingData.totalCount) { try { searchState.value = WalletsData.append(searchListingData.wallets) - val searchResponse = getWalletsUseCase(sdkType = W3M_SDK, search = searchPhrase, page = searchListingData.page + 1, excludeIds = web3ModalEngine.excludedWalletsIds) + val searchResponse = getWalletsUseCase(sdkType = W3M_SDK, search = searchPhrase, page = searchListingData.page + 1, excludeIds = appKitEngine.excludedWalletsIds) searchListingData.addNextPage(searchResponse.wallets) searchState.value = WalletsData.submit(searchListingData.wallets) } catch (exception: Exception) { diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/all_wallets/AllWalletsRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/all_wallets/AllWalletsRoute.kt new file mode 100644 index 000000000..637628195 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/all_wallets/AllWalletsRoute.kt @@ -0,0 +1,310 @@ +package com.reown.appkit.ui.routes.connect.all_wallets + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyGridScope +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.rememberLazyGridState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.modal.utils.isLandscape +import com.reown.util.Empty +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.ContentDescription +import com.reown.appkit.ui.components.internal.commons.HorizontalSpacer +import com.reown.appkit.ui.components.internal.commons.LoadingSpinner +import com.reown.appkit.ui.components.internal.commons.ScanQRIcon +import com.reown.appkit.ui.components.internal.commons.TransparentSurface +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.inputs.SearchInput +import com.reown.appkit.ui.components.internal.commons.inputs.SearchState +import com.reown.appkit.ui.components.internal.commons.inputs.SearchStatePreviewProvider +import com.reown.appkit.ui.components.internal.commons.walletsGridItems +import com.reown.modal.ui.model.LoadingState +import com.reown.appkit.ui.previews.ComponentPreview +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.routes.connect.WalletsData +import com.reown.appkit.ui.theme.AppKitTheme +import com.reown.appkit.ui.utils.conditionalModifier +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch + +@Composable +internal fun AllWalletsRoute( + connectViewModel: ConnectViewModel +) { + val walletsState by connectViewModel.walletsState.collectAsState() + + AllWalletsContent( + walletsData = walletsState, + searchPhrase = connectViewModel.searchPhrase, + onSearch = { connectViewModel.search(it) }, + onSearchClear = { connectViewModel.clearSearch() }, + onFetchNextPage = { connectViewModel.fetchMoreWallets() }, + onWalletItemClick = { wallet -> connectViewModel.navigateToRedirectRoute(wallet) }, + onScanQRClick = { connectViewModel.navigateToScanQRCode() } + ) +} + +@Composable +private fun AllWalletsContent( + walletsData: WalletsData, + searchPhrase: String, + onSearch: (String) -> Unit, + onSearchClear: () -> Unit, + onFetchNextPage: () -> Unit, + onWalletItemClick: (Wallet) -> Unit, + onScanQRClick: () -> Unit +) { + val gridState = rememberLazyGridState() + val coroutineScope = rememberCoroutineScope() + val scrollToFirstItem = { coroutineScope.launch { gridState.scrollToItem(0) } } + val searchState = remember { + SearchState( + searchPhrase = searchPhrase, + onSearchSubmit = { onSearch(it).also { scrollToFirstItem() } }, + onClearInput = { onSearchClear().also { scrollToFirstItem() } } + ) + } + val gridFraction = if (isLandscape) 1f else .95f + + LaunchedEffect(gridState) { + snapshotFlow { gridState.firstVisibleItemIndex != 0 && !gridState.canScrollForward } + .distinctUntilChanged() + .filter { it } + .collect { onFetchNextPage() } + } + + Column(modifier = Modifier.fillMaxHeight(gridFraction)) { + SearchInputRow(searchState, onScanQRClick) + if (walletsData.loadingState == LoadingState.REFRESH) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + LoadingSpinner() + } + } else if (walletsData.wallets.isEmpty()) { + NoWalletsFoundItem() + } else { + WalletsGrid(gridState, walletsData, onWalletItemClick) + } + } +} + +@Composable +private fun WalletsGrid( + gridState: LazyGridState, + walletsData: WalletsData, + onWalletItemClick: (Wallet) -> Unit +) { + val color = AppKitTheme.colors.background.color275 + Box { + LazyVerticalGrid( + state = gridState, + columns = GridCells.FixedSize(82.dp), + modifier = Modifier + .padding(horizontal = 10.dp) + .graphicsLayer { alpha = 0.99f } + .drawWithContent { + val colors = listOf(Color.Transparent, color) + drawContent() + drawRect( + brush = Brush.verticalGradient(colors, startY = 0f, endY = 40f), + blendMode = BlendMode.DstIn, + ) + }, + verticalArrangement = Arrangement.Center, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + walletsGridItems(walletsData.wallets, onWalletItemClick) + if (walletsData.loadingState == LoadingState.APPEND) { + loadingWalletsItems() + } + } + } +} + +private fun LazyGridScope.loadingWalletsItems() { + items(10) { + TransparentSurface( + modifier = Modifier.padding(4.dp), + shape = RoundedCornerShape(16.dp) + ) { + Column( + modifier = Modifier + .width(76.dp) + .height(96.dp) + .background(AppKitTheme.colors.grayGlass02), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(id = R.drawable.wallet_placeholder), + contentDescription = "Wallet loader", + modifier = Modifier + .size(54.dp) + .clip(RoundedCornerShape(16.dp)) + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass10, shape = RoundedCornerShape(16.dp)) + + ) + VerticalSpacer(height = 8.dp) + Text( + text = String.Empty, + style = AppKitTheme.typo.tiny500, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = 2.dp) + ) + } + } + + } +} + +@Composable +private fun SearchInputRow( + searchState: SearchState, + onScanQRClick: () -> Unit +) { + val defaultSpacing: Dp = 12.dp + val focusBorderWidth: Dp = 4.dp + val focusedSpacing: Dp = defaultSpacing - focusBorderWidth + val focusBorderColor = AppKitTheme.colors.accent20 + val state by searchState.state.collectAsState() + + val paddingValues: PaddingValues + val spacerValue: Dp + if (state.isFocused) { + spacerValue = focusedSpacing + paddingValues = PaddingValues(start = focusedSpacing, top = focusedSpacing, bottom = focusedSpacing, end = defaultSpacing) + } else { + spacerValue = 12.dp + paddingValues = PaddingValues(12.dp) + } + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(paddingValues) + ) { + Box(modifier = Modifier + .weight(1f) + .conditionalModifier(state.isFocused) { + border(width = focusBorderWidth, color = focusBorderColor, RoundedCornerShape(16.dp)).padding(focusBorderWidth) + }) { + SearchInput(searchState) + } + HorizontalSpacer(width = spacerValue) + ScanQRIcon(onClick = onScanQRClick) + } +} + + +@Composable +private fun ColumnScope.NoWalletsFoundItem() { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_wallet), + contentDescription = ContentDescription.WALLET.description, + modifier = Modifier + .size(40.dp) + .background(AppKitTheme.colors.grayGlass05, RoundedCornerShape(12.dp)) + .padding(7.dp) + ) + VerticalSpacer(height = 20.dp) + Text( + text = "No Wallet found", + style = TextStyle(color = AppKitTheme.colors.foreground.color125, fontSize = 16.sp), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + +} + +@UiModePreview +@Composable +private fun SearchRowPreview( + @PreviewParameter(SearchStatePreviewProvider::class) state: SearchState +) { + ComponentPreview { SearchInputRow(searchState = state, {}) } +} + +@UiModePreview +@Composable +private fun AllWalletsEmptyPreview() { + AppKitPreview { + AllWalletsContent(WalletsData.empty(), "", {}, {}, {}, {}, {}) + } +} + +@UiModePreview +@Composable +private fun AllWalletsPreview() { + AppKitPreview { + AllWalletsContent(WalletsData.submit(testWallets),"", {}, {}, {}, {}, {}) + } +} + +@UiModePreview +@Composable +private fun AllWalletsLoadingRefreshPreview() { + AppKitPreview { + AllWalletsContent(WalletsData.refresh(),"", {}, {}, {}, {}, {}) + } +} + +@UiModePreview +@Composable +private fun AllWalletsLoadingAppendPreview() { + AppKitPreview { + AllWalletsContent(WalletsData.append(testWallets),"", {}, {}, {}, {}, {}) + } +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/choose_network/ChooseNetworkRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/choose_network/ChooseNetworkRoute.kt similarity index 82% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/choose_network/ChooseNetworkRoute.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/choose_network/ChooseNetworkRoute.kt index 34463c8a4..6c017c6e2 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/choose_network/ChooseNetworkRoute.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/choose_network/ChooseNetworkRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.routes.connect.choose_network +package com.reown.appkit.ui.routes.connect.choose_network import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -13,18 +13,18 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.ui.components.internal.commons.NetworkBottomSection -import com.walletconnect.web3.modal.ui.components.internal.commons.network.ChainNetworkItem -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.utils.getChainNetworkImageUrl +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.ui.components.internal.commons.NetworkBottomSection +import com.reown.appkit.ui.components.internal.commons.network.ChainNetworkItem +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.utils.getChainNetworkImageUrl @Composable internal fun ChooseNetworkRoute( connectViewModel: ConnectViewModel ) { - val chains = Web3Modal.chains + val chains = AppKit.chains val selectedChain by connectViewModel.selectedChain.collectAsState(initial = null) ChainNetworkSelector( diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt new file mode 100644 index 000000000..058aacbf3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt @@ -0,0 +1,124 @@ +package com.reown.appkit.ui.routes.connect.connect_wallet + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.appkit.ui.components.internal.ErrorModalState +import com.reown.appkit.ui.components.internal.commons.InstalledWalletIcon +import com.reown.appkit.ui.components.internal.commons.ListSelectRow +import com.reown.appkit.ui.components.internal.commons.RecentLabel +import com.reown.appkit.ui.components.internal.commons.WalletImage +import com.reown.appkit.ui.components.internal.walletconnect.allWallets +import com.reown.appkit.ui.model.UiStateBuilder +import com.reown.appkit.ui.previews.ConnectYourWalletPreviewProvider +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun ConnectWalletRoute( + connectViewModel: ConnectViewModel +) { + UiStateBuilder( + connectViewModel.uiState, + onError = { ErrorModalState { connectViewModel.fetchInitialWallets() } } + ) { + ConnectWalletContent( + wallets = it, + walletsTotalCount = connectViewModel.getWalletsTotalCount(), + onWalletItemClick = { wallet -> connectViewModel.navigateToRedirectRoute(wallet) }, + onViewAllClick = { connectViewModel.navigateToAllWallets() }, + ) + } +} + +@Composable +private fun ConnectWalletContent( + wallets: List, + walletsTotalCount: Int, + onWalletItemClick: (Wallet) -> Unit, + onViewAllClick: () -> Unit, +) { + WalletsList( + wallets = wallets, + walletsTotalCount = walletsTotalCount, + onWalletItemClick = onWalletItemClick, + onViewAllClick = onViewAllClick, + ) +} + +@Composable +private fun WalletsList( + wallets: List, + walletsTotalCount: Int, + onWalletItemClick: (Wallet) -> Unit, + onViewAllClick: () -> Unit +) { + LazyColumn( + modifier = Modifier.fillMaxWidth(), + contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) + ) { + itemsIndexed(items = wallets.take(4)) { _, item -> + WalletListSelect(item, onWalletItemClick) + } + allWallets(text = walletSizeLabel(walletsTotalCount), onClick = onViewAllClick) + } +} + +private fun walletSizeLabel(total: Int): String = with(total % 10) { + if (this != 0) { + "${total - this}+" + } else { + total.toString() + } +} + +@Composable +private fun WalletListSelect(item: Wallet, onWalletItemClick: (Wallet) -> Unit) { + val label: (@Composable (Boolean) -> Unit)? = when { + item.isRecent -> { + { RecentLabel(it) } + } + else -> null + } + + ListSelectRow( + startIcon = { + Box { + WalletImage( + url = item.imageUrl, + modifier = Modifier + .size(40.dp) + .border(width = 1.dp, color = AppKitTheme.colors.grayGlass10, shape = RoundedCornerShape(12.dp)) + .clip(RoundedCornerShape(12.dp)) + ) + if (item.isWalletInstalled) { + InstalledWalletIcon() + } + } + }, + text = item.name, + onClick = { onWalletItemClick(item) }, + contentPadding = PaddingValues(vertical = 4.dp), + label = label + ) +} + +@UiModePreview +@Composable +private fun ConnectYourWalletPreview( + @PreviewParameter(ConnectYourWalletPreviewProvider::class) wallets: List +) { + AppKitPreview(title = "Connect Wallet") { + ConnectWalletContent(wallets, 200, {}, {}) + } +} diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/get_wallet/GetAWalletRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/get_wallet/GetAWalletRoute.kt new file mode 100644 index 000000000..6cecefeae --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/get_wallet/GetAWalletRoute.kt @@ -0,0 +1,77 @@ +package com.reown.appkit.ui.routes.connect.get_wallet + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.unit.dp +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.modal.utils.openPlayStore +import com.reown.appkit.ui.components.internal.commons.AllWalletsIcon +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.ListSelectRow +import com.reown.appkit.ui.components.internal.commons.WalletImage +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.testWallets + +@Composable +internal fun GetAWalletRoute(wallets: List) { + GetAWalletContent( + wallets = wallets, + ) +} + +@Composable +private fun GetAWalletContent( + wallets: List, +) { + val uriHandler = LocalUriHandler.current + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 12.dp) + ) { + itemsIndexed(wallets.take(5)) { _, wallet -> + ListSelectRow( + startIcon = { + WalletImage( + url = wallet.imageUrl, + modifier = Modifier + .size(40.dp) + .clip(RoundedCornerShape(10.dp)) + ) + }, + text = wallet.name, + contentPadding = PaddingValues(vertical = 4.dp), + onClick = { uriHandler.openPlayStore(wallet.playStore) } + ) + } + item { + ListSelectRow( + startIcon = { AllWalletsIcon() }, + text = "Explore all", + contentPadding = PaddingValues(vertical = 4.dp), + label = { ExternalIcon() }, + onClick = { uriHandler.openUri("https://explorer.walletconnect.com/?type=wallet") } + ) + } + } +} + +@UiModePreview +@Composable +private fun PreviewGetAWallet() { + AppKitPreview(title = "Get a Wallet") { + GetAWalletContent(wallets = testWallets) + } +} + diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectState.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectState.kt new file mode 100644 index 000000000..25441cc1c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectState.kt @@ -0,0 +1,8 @@ +package com.reown.appkit.ui.routes.connect.redirect + +sealed class RedirectState { + object Loading: RedirectState() + object Reject: RedirectState() + object Expired: RedirectState() + object NotDetected: RedirectState() +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectWalletScreen.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectWalletScreen.kt similarity index 80% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectWalletScreen.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectWalletScreen.kt index 401695cfe..4ed8fa2e6 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectWalletScreen.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/redirect/RedirectWalletScreen.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.routes.connect.redirect +package com.reown.appkit.ui.routes.connect.redirect import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background @@ -29,37 +29,37 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.pulse.model.ConnectionMethod -import com.walletconnect.modal.utils.openMobileLink -import com.walletconnect.modal.utils.openPlayStore -import com.walletconnect.modal.utils.openWebAppLink -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.engine.coinbase.isCoinbaseWallet -import com.walletconnect.web3.modal.ui.components.internal.OrientationBox -import com.walletconnect.web3.modal.ui.components.internal.commons.DeclinedIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.RoundedWalletImage -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletImageWithLoader -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ChipButton -import com.walletconnect.web3.modal.ui.components.internal.commons.button.TryAgainButton -import com.walletconnect.web3.modal.ui.components.internal.commons.entry.CopyActionEntry -import com.walletconnect.web3.modal.ui.components.internal.commons.entry.StoreEntry -import com.walletconnect.web3.modal.ui.components.internal.commons.switch.PlatformTab -import com.walletconnect.web3.modal.ui.components.internal.commons.switch.PlatformTabRow -import com.walletconnect.web3.modal.ui.components.internal.commons.switch.rememberWalletPlatformTabs -import com.walletconnect.web3.modal.ui.components.internal.snackbar.LocalSnackBarHandler -import com.walletconnect.web3.modal.ui.previews.Landscape -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.android.pulse.model.ConnectionMethod +import com.reown.modal.utils.openMobileLink +import com.reown.modal.utils.openPlayStore +import com.reown.modal.utils.openWebAppLink +import com.reown.util.Empty +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.engine.coinbase.isCoinbaseWallet +import com.reown.appkit.ui.components.internal.OrientationBox +import com.reown.appkit.ui.components.internal.commons.DeclinedIcon +import com.reown.appkit.ui.components.internal.commons.ExternalIcon +import com.reown.appkit.ui.components.internal.commons.RoundedWalletImage +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.WalletImageWithLoader +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ChipButton +import com.reown.appkit.ui.components.internal.commons.button.TryAgainButton +import com.reown.appkit.ui.components.internal.commons.entry.CopyActionEntry +import com.reown.appkit.ui.components.internal.commons.entry.StoreEntry +import com.reown.appkit.ui.components.internal.commons.switch.PlatformTab +import com.reown.appkit.ui.components.internal.commons.switch.PlatformTabRow +import com.reown.appkit.ui.components.internal.commons.switch.rememberWalletPlatformTabs +import com.reown.appkit.ui.components.internal.snackbar.LocalSnackBarHandler +import com.reown.appkit.ui.previews.Landscape +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.theme.AppKitTheme @Composable internal fun RedirectWalletRoute( @@ -88,7 +88,7 @@ internal fun RedirectWalletRoute( LaunchedEffect(Unit) { - Web3ModalDelegate.wcEventModels.collect { + AppKitDelegate.wcEventModels.collect { when (it) { is Modal.Model.RejectedSession, is Modal.Model.SessionAuthenticateResponse.Error -> { redirectState = RedirectState.Reject @@ -294,30 +294,30 @@ private fun RedirectLabel(state: RedirectState, wallet: Wallet) { when (state) { RedirectState.Loading -> { header = "Continue in ${wallet.name}" - headerStyle = Web3ModalTheme.typo.paragraph500 + headerStyle = AppKitTheme.typo.paragraph500 description = "Accept connection request in your wallet app" - descriptionStyle = Web3ModalTheme.typo.small400.copy(color = Web3ModalTheme.colors.foreground.color200) + descriptionStyle = AppKitTheme.typo.small400.copy(color = AppKitTheme.colors.foreground.color200) } RedirectState.Reject -> { header = "Connection declined" description = "Connection can be declined if a previous request is still active" - headerStyle = Web3ModalTheme.typo.paragraph400.copy(Web3ModalTheme.colors.error) - descriptionStyle = Web3ModalTheme.typo.small400.copy(color = Web3ModalTheme.colors.foreground.color200) + headerStyle = AppKitTheme.typo.paragraph400.copy(AppKitTheme.colors.error) + descriptionStyle = AppKitTheme.typo.small400.copy(color = AppKitTheme.colors.foreground.color200) } RedirectState.Expired -> { header = "Connection expired" description = String.Empty - headerStyle = Web3ModalTheme.typo.paragraph400.copy(Web3ModalTheme.colors.error) - descriptionStyle = Web3ModalTheme.typo.small400.copy(color = Web3ModalTheme.colors.foreground.color200) + headerStyle = AppKitTheme.typo.paragraph400.copy(AppKitTheme.colors.error) + descriptionStyle = AppKitTheme.typo.small400.copy(color = AppKitTheme.colors.foreground.color200) } RedirectState.NotDetected -> { header = "App not installed" description = String.Empty - headerStyle = Web3ModalTheme.typo.paragraph400 - descriptionStyle = Web3ModalTheme.typo.small400.copy(color = Web3ModalTheme.colors.foreground.color200) + headerStyle = AppKitTheme.typo.paragraph400 + descriptionStyle = AppKitTheme.typo.small400.copy(color = AppKitTheme.colors.foreground.color200) } } Column( @@ -384,11 +384,11 @@ private fun RedirectWebWalletScreen( Column( horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Continue in ${wallet.name}", style = Web3ModalTheme.typo.paragraph400) + Text(text = "Continue in ${wallet.name}", style = AppKitTheme.typo.paragraph400) VerticalSpacer(height = 8.dp) Text( text = "Accept connection request in the wallet", - style = Web3ModalTheme.typo.small400.copy(color = Web3ModalTheme.colors.foreground.color200), + style = AppKitTheme.typo.small400.copy(color = AppKitTheme.colors.foreground.color200), textAlign = TextAlign.Center ) VerticalSpacer(height = 20.dp) @@ -455,7 +455,7 @@ private fun RejectWalletImage(url: String) { Box( modifier = Modifier .align(Alignment.BottomEnd) - .background(Web3ModalTheme.colors.background.color125, shape = CircleShape) + .background(AppKitTheme.colors.background.color125, shape = CircleShape) .padding(2.dp) ) { DeclinedIcon() @@ -468,7 +468,7 @@ private fun RejectWalletImage(url: String) { @Composable private fun PreviewRedirectWalletScreenWithLoadingState() { val wallet = testWallets.first() - Web3ModalPreview(wallet.name) { + AppKitPreview(wallet.name) { RedirectWalletScreen(redirectState = RedirectState.Loading, platformTab = PlatformTab.MOBILE, {}, wallet, {}, {}, {}, {}) } } @@ -478,7 +478,7 @@ private fun PreviewRedirectWalletScreenWithLoadingState() { @Composable private fun PreviewRedirectWalletScreenWithRejectedState() { val wallet = testWallets.first() - Web3ModalPreview(wallet.name) { + AppKitPreview(wallet.name) { RedirectWalletScreen(redirectState = RedirectState.Reject, platformTab = PlatformTab.MOBILE, {}, wallet, {}, {}, {}, {}) } } @@ -488,7 +488,7 @@ private fun PreviewRedirectWalletScreenWithRejectedState() { @Composable private fun PreviewRedirectWalletScreenWithExpiredState() { val wallet = testWallets.first() - Web3ModalPreview(wallet.name) { + AppKitPreview(wallet.name) { RedirectWalletScreen(redirectState = RedirectState.Expired, platformTab = PlatformTab.MOBILE, {}, wallet, {}, {}, {}, {}) } } @@ -498,7 +498,7 @@ private fun PreviewRedirectWalletScreenWithExpiredState() { @Composable private fun PreviewRedirectWalletScreenWithNotDetectedState() { val wallet = testWallets.first() - Web3ModalPreview(wallet.name) { + AppKitPreview(wallet.name) { RedirectWalletScreen(redirectState = RedirectState.NotDetected, platformTab = PlatformTab.MOBILE, {}, wallet, {}, {}, {}, {}) } } @@ -508,7 +508,7 @@ private fun PreviewRedirectWalletScreenWithNotDetectedState() { @Composable private fun PreviewRedirectWebWalletScreen() { val wallet = testWallets.first() - Web3ModalPreview(wallet.name) { + AppKitPreview(wallet.name) { RedirectWalletScreen(redirectState = RedirectState.Loading, platformTab = PlatformTab.WEB, {}, wallet, {}, {}, {}, {}) } } diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/scan_code/ScanCodeRoute.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/scan_code/ScanCodeRoute.kt new file mode 100644 index 000000000..52619680f --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/scan_code/ScanCodeRoute.kt @@ -0,0 +1,170 @@ +package com.reown.appkit.ui.routes.connect.scan_code + +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ClipboardManager +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.reown.android.pulse.model.ConnectionMethod +import com.reown.modal.ui.components.qr.QrCodeType +import com.reown.modal.ui.components.qr.WalletConnectQRCode +import com.reown.appkit.client.Modal +import com.reown.appkit.domain.delegate.AppKitDelegate +import com.reown.appkit.ui.components.internal.OrientationBox +import com.reown.appkit.ui.components.internal.commons.entry.CopyActionEntry +import com.reown.appkit.ui.components.internal.snackbar.LocalSnackBarHandler +import com.reown.appkit.ui.previews.Landscape +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.routes.connect.ConnectViewModel +import com.reown.appkit.ui.theme.AppKitTheme +import kotlinx.coroutines.flow.filter + +@Composable +internal fun ScanQRCodeRoute(connectViewModel: ConnectViewModel) { + val snackBarHandler = LocalSnackBarHandler.current + val clipboardManager: ClipboardManager = LocalClipboardManager.current + var uri by remember { mutableStateOf(connectViewModel.uri) } + + LaunchedEffect(Unit) { + AppKitDelegate + .wcEventModels + .filter { event -> event is Modal.Model.RejectedSession || event is Modal.Model.SessionAuthenticateResponse.Error } + .collect { + snackBarHandler.showErrorSnack("Declined") + connectViewModel.connectWalletConnect(name = "WalletConnect", method = ConnectionMethod.QR_CODE, linkMode = null) { newUri -> uri = newUri } + } + } + + ScanQRCodeContent( + uri = uri, + onCopyLinkClick = { + snackBarHandler.showSuccessSnack("Link copied") + clipboardManager.setText(AnnotatedString(connectViewModel.uri)) + } + ) +} + +@Composable +private fun ScanQRCodeContent( + uri: String, onCopyLinkClick: () -> Unit +) { + OrientationBox( + portrait = { PortraitContent(uri, onCopyLinkClick) }, + landscape = { LandscapeContent(uri, onCopyLinkClick) } + ) + +} + +@Composable +private fun LandscapeContent( + uri: String, + onCopyLinkClick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 5.dp, horizontal = 10.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .weight(1f) + .padding(vertical = 5.dp), + contentAlignment = Alignment.Center + ) { + QRCode(uri = uri) + } + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally + ) { + ScanQrCodeLabel() + Spacer(modifier = Modifier.height(12.dp)) + CopyActionEntry(onClick = onCopyLinkClick) + } + } +} + +@Composable +private fun PortraitContent( + uri: String, + onCopyLinkClick: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + QRCode(uri = uri) + Spacer(modifier = Modifier.height(20.dp)) + ScanQrCodeLabel() + Spacer(modifier = Modifier.height(12.dp)) + CopyActionEntry(onClick = onCopyLinkClick) + } +} + +@Composable +private fun ScanQrCodeLabel() { + Text( + text = "Scan this QR code with your phone", + modifier = Modifier.fillMaxWidth(), style = AppKitTheme.typo.paragraph400, textAlign = TextAlign.Center + ) +} + +@Composable +private fun QRCode(uri: String) { + if (isSystemInDarkTheme()) { + Box( + modifier = Modifier + .background(AppKitTheme.colors.inverse100, shape = RoundedCornerShape(36.dp)) + .padding(16.dp) + ) { + WalletConnectQRCode( + qrData = uri, + primaryColor = AppKitTheme.colors.inverse000, + logoColor = AppKitTheme.colors.accent100, + type = QrCodeType.W3M + ) + } + } else { + WalletConnectQRCode( + qrData = uri, + primaryColor = AppKitTheme.colors.inverse000, + logoColor = AppKitTheme.colors.accent100, + type = QrCodeType.W3M + ) + } +} + +@UiModePreview +@Landscape +@Composable +private fun ScanQRCodePreview() { + AppKitPreview("Mobile Wallets") { + ScanQRCodeContent("47442c19ea7c6a7a836fa3e53af1ddd375438daaeea9acdbf595e989a731b73249a10a7cc0e343ca627e536609", {}) + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/what_is_wallet/WhatIsWallet.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/what_is_wallet/WhatIsWallet.kt new file mode 100644 index 000000000..e3abea0a3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/routes/connect/what_is_wallet/WhatIsWallet.kt @@ -0,0 +1,87 @@ +package com.reown.appkit.ui.routes.connect.what_is_wallet + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pulse.domain.SendEventInterface +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Props +import com.reown.appkit.R +import com.reown.appkit.ui.components.internal.commons.HelpSection +import com.reown.appkit.ui.components.internal.commons.VerticalSpacer +import com.reown.appkit.ui.components.internal.commons.WalletIcon +import com.reown.appkit.ui.components.internal.commons.button.ButtonSize +import com.reown.appkit.ui.components.internal.commons.button.ButtonStyle +import com.reown.appkit.ui.components.internal.commons.button.ImageButton +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.UiModePreview +import com.reown.appkit.ui.previews.AppKitPreview +import com.reown.appkit.ui.theme.AppKitTheme + +@Composable +internal fun WhatIsWallet( + navController: NavController +) { + val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() + WhatIsWallet { + sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_GET_WALLET)) + navController.navigate(Route.GET_A_WALLET.path) + } +} + +@Composable +private fun WhatIsWallet( + onGetAWalletClick: () -> Unit +) { + Column( + modifier = Modifier + .padding(horizontal = 20.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + VerticalSpacer(20.dp) + HelpSection( + title = "One login for all of web3", + body = "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", + assets = listOf(R.drawable.login, R.drawable.profile, R.drawable.lock) + ) + VerticalSpacer(24.dp) + HelpSection( + title = "A home for your digital assets", + body = "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", + assets = listOf(R.drawable.defi, R.drawable.nft, R.drawable.eth) + ) + VerticalSpacer(24.dp) + HelpSection( + title = "Your gateway to a new web", + body = "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", + assets = listOf(R.drawable.browser, R.drawable.noun, R.drawable.dao) + ) + VerticalSpacer(20.dp) + ImageButton( + text = "Get a wallet", + image = { WalletIcon(AppKitTheme.colors.inverse100) }, + style = ButtonStyle.MAIN, + size = ButtonSize.S, + paddingValues = PaddingValues(start = 8.dp, top = 6.dp, end = 12.dp, 6.dp), + onClick = { onGetAWalletClick() } + ) + VerticalSpacer(30.dp) + } +} + +@Composable +@UiModePreview +private fun HelpContentPreview() { + AppKitPreview("What is a Wallet?") { + WhatIsWallet {} + } +} \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/AppKitColors.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/AppKitColors.kt new file mode 100644 index 000000000..ceb7c6095 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/AppKitColors.kt @@ -0,0 +1,163 @@ +package com.reown.appkit.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.reown.appkit.ui.AppKitTheme + +internal data class AppKitColors( + val accent100: Color, + val accent90: Color, + val accent80: Color, + val foreground: ColorPalette, + val background: ColorPalette, + val grayGlass: Color, + val success: Color, + val error: Color, + val teal: Color, + val magenta: Color, + val indigo: Color, + val orange: Color, + val purple: Color +) { + val inverse100 = Color.White + val inverse000 = Color.Black + + val success15 = success.copy(.15f) + + val accent20: Color = accent100.copy(.2f) + val accent15: Color = accent100.copy(.15f) + val accent10: Color = accent100.copy(.1f) + + val grayGlass02: Color = grayGlass.copy(.02f) + val grayGlass05: Color = grayGlass.copy(.05f) + val grayGlass10: Color = grayGlass.copy(.10f) + val grayGlass15: Color = grayGlass.copy(.15f) + val grayGlass20: Color = grayGlass.copy(.2f) + val grayGlass25: Color = grayGlass.copy(.25f) + val grayGlass30: Color = grayGlass.copy(.3f) +} +data class ColorPalette( + val color100: Color, + val color125: Color, + val color150: Color, + val color175: Color, + val color200: Color, + val color225: Color, + val color250: Color, + val color275: Color, + val color300: Color, +) +@Composable +internal fun provideAppKitColors(composition: CustomComposition): AppKitColors = if (composition.mode.isDarkTheme()) { + AppKitColors( + accent100 = composition.darkColors.accent100, + accent90 = composition.darkColors.accent90, + accent80 = composition.darkColors.accent80, + foreground = composition.darkColors.foreground, + background = composition.darkColors.background, + grayGlass = composition.darkColors.grayGlass, + success = composition.darkColors.success, + error = composition.darkColors.error, + teal = defaultDarkAppKitColors.teal, + magenta = defaultDarkAppKitColors.magenta, + indigo = defaultDarkAppKitColors.indigo, + orange = defaultDarkAppKitColors.orange, + purple = defaultDarkAppKitColors.purple + ) +} else { + AppKitColors( + accent100 = composition.lightColors.accent100, + accent90 = composition.lightColors.accent90, + accent80 = composition.lightColors.accent80, + foreground = composition.lightColors.foreground, + background = composition.lightColors.background, + grayGlass = composition.lightColors.grayGlass, + success = composition.lightColors.success, + error = composition.lightColors.error, + teal = defaultLightAppKitColors.teal, + magenta = defaultLightAppKitColors.magenta, + indigo = defaultLightAppKitColors.indigo, + orange = defaultLightAppKitColors.orange, + purple = defaultLightAppKitColors.purple + ) +} + +@Composable +private fun AppKitTheme.Mode.isDarkTheme() = when(this) { + AppKitTheme.Mode.LIGHT -> false + AppKitTheme.Mode.DARK -> true + AppKitTheme.Mode.AUTO -> isSystemInDarkTheme() +} + +internal val defaultDarkAppKitColors = AppKitColors( + accent100 = Color(0xFF47A1FF), + accent90 = Color(0xFF59AAFF), + accent80 = Color(0xFF6CB4FF), + foreground = ColorPalette( + Color(0xFFE4E7E7), + Color(0xFFD0D5D5), + Color(0xFFA8B1B1), + Color(0xFFA8B0B0), + Color(0xFF949E9E), + Color(0xFF868F8F), + Color(0xFF788080), + Color(0xFF788181), + Color(0xFF637777) + ), + background = ColorPalette( + Color(0xFF141414), + Color(0xFF191A1A), + Color(0xFF1E1F1F), + Color(0xFF222525), + Color(0xFF272A2A), + Color(0xFF2C3030), + Color(0xFF313535), + Color(0xFF363B3B), + Color(0xFF3B4040) + ), + grayGlass = Color(0xFFFFFFFF), + success = Color(0xFF26D962), + error = Color(0xFFF25A67), + teal = Color(0xFF36E2E2), + magenta = Color(0xFFCB4D8C), + indigo = Color(0xFF516DFB), + orange = Color(0xFFFFA64C), + purple = Color(0xFF9063F7), +) + +internal val defaultLightAppKitColors = AppKitColors( + accent100 = Color(0xFF3396FF), + accent90 = Color(0xFF2D7DD2), + accent80 = Color(0xFF2978CC), + foreground = ColorPalette( + Color(0xFF141414), + Color(0xFF2D3131), + Color(0xFF474D4D), + Color(0xFF636D6D), + Color(0xFF798686), + Color(0xFF828F8F), + Color(0xFF8B9797), + Color(0xFF95A0A0), + Color(0xFF9EA9A9) + ), + background = ColorPalette( + Color(0xFFFFFFFF), + Color(0xFFF5FAFA), + Color(0xFFF3F8F8), + Color(0xFFEEF4F4), + Color(0xFFEAF1F1), + Color(0xFFE5EDED), + Color(0xFFE1E9E9), + Color(0xFFDCE7E7), + Color(0xFFD8E3E3) + ), + grayGlass = Color(0xFF000000), + success = Color(0xFF26B562), + error = Color(0xFFF05142), + teal = Color(0xFF2BB6B6), + magenta = Color(0xFFC65380), + indigo = Color(0xFF3D5CF5), + orange = Color(0xFFEA8C2E), + purple = Color(0xFF794CFF), +) diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/CustomComposition.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/CustomComposition.kt new file mode 100644 index 000000000..25a65e04a --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/CustomComposition.kt @@ -0,0 +1,12 @@ +package com.reown.appkit.ui.theme + +import androidx.compose.runtime.compositionLocalOf +import com.reown.appkit.ui.AppKitTheme + +internal data class CustomComposition( + val mode: AppKitTheme.Mode = AppKitTheme.Mode.AUTO, + val lightColors: AppKitTheme.Colors = AppKitTheme.provideLightAppKitColors(), + val darkColors: AppKitTheme.Colors = AppKitTheme.provideDarkAppKitColor(), +) + +internal val LocalCustomComposition = compositionLocalOf { CustomComposition() } \ No newline at end of file diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Theme.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Theme.kt new file mode 100644 index 000000000..1a4988992 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Theme.kt @@ -0,0 +1,37 @@ +package com.reown.appkit.ui.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf + +@Composable +internal fun ProvideAppKitThemeComposition( + content: @Composable () -> Unit, +) { + val composition = LocalCustomComposition.current + val colors = provideAppKitColors(composition) + val typography = provideDefaultTypography(colors) + CompositionLocalProvider( + LocalColorsComposition provides colors, + LocalTypographyComposition provides typography, + content = content + ) +} + +internal object AppKitTheme { + val colors: AppKitColors + @Composable + get() = LocalColorsComposition.current + + val typo: AppKitTypography + @Composable + get() = LocalTypographyComposition.current +} + +private val LocalTypographyComposition = compositionLocalOf { + error("No typography provided") +} + +private val LocalColorsComposition = compositionLocalOf { + error("No colors provided") +} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Typography.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Typography.kt similarity index 96% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Typography.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Typography.kt index c4cc9a679..284331191 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Typography.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/theme/Typography.kt @@ -1,10 +1,10 @@ -package com.walletconnect.web3.modal.ui.theme +package com.reown.appkit.ui.theme import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -internal fun provideDefaultTypography(colors: Web3ModalColors) = Web3ModalTypography( +internal fun provideDefaultTypography(colors: AppKitColors) = AppKitTypography( mediumTitle400 = TextStyle( fontWeight = FontWeight.Normal, fontSize = 24.sp, @@ -122,7 +122,7 @@ internal fun provideDefaultTypography(colors: Web3ModalColors) = Web3ModalTypogr ), ) -internal data class Web3ModalTypography( +internal data class AppKitTypography( val mediumTitle400: TextStyle, val mediumTitle500: TextStyle, val mediumTitle600: TextStyle, diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Lifecycle.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Lifecycle.kt similarity index 91% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Lifecycle.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Lifecycle.kt index 017cb533f..eaf235438 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Lifecycle.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Lifecycle.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.utils +package com.reown.appkit.ui.utils import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -6,7 +6,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner -import com.walletconnect.web3.modal.ui.components.ComponentDelegate +import com.reown.appkit.ui.components.ComponentDelegate @Composable internal fun ComposableLifecycleEffect( diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Modifier.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Modifier.kt similarity index 81% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Modifier.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Modifier.kt index cfb4017b2..a0baadce4 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Modifier.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Modifier.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.ui.utils +package com.reown.appkit.ui.utils import androidx.compose.ui.Modifier diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Navigation.kt b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Navigation.kt new file mode 100644 index 000000000..2de98d98c --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/ui/utils/Navigation.kt @@ -0,0 +1,60 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.appkit.ui.utils + +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.VisibilityThreshold +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.AnimatedNavHost +import com.google.accompanist.navigation.animation.composable + +@Composable +internal fun AnimatedNavGraph( + navController: NavHostController, + startDestination: String, + builder: NavGraphBuilder.() -> Unit +) { + val stiffnessAnimSpec = spring(stiffness = Spring.StiffnessLow, visibilityThreshold = IntOffset.VisibilityThreshold) + val tweenAnimSpec = tween(durationMillis = 400) + + AnimatedNavHost( + navController = navController, + startDestination = startDestination, + contentAlignment = Alignment.BottomCenter, + enterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeIn(animationSpec = tweenAnimSpec) }, + exitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) + fadeOut(animationSpec = tweenAnimSpec) }, + popEnterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) + fadeIn(animationSpec = tweenAnimSpec) }, + popExitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeOut(animationSpec = tweenAnimSpec) }, + modifier = Modifier.animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMedium, visibilityThreshold = IntSize.VisibilityThreshold)), + builder = builder + ) +} + +internal fun NavGraphBuilder.animatedComposable(route: String, content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit) { + val stiffnessAnimSpec = spring(stiffness = Spring.StiffnessLow, visibilityThreshold = IntOffset.VisibilityThreshold) + val tweenAnimSpec = tween(durationMillis = 400) + composable( + route = route, + content = content, + enterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeIn(animationSpec = tweenAnimSpec) }, + exitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) + fadeOut(animationSpec = tweenAnimSpec) }, + popEnterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) + fadeIn(animationSpec = tweenAnimSpec) }, + popExitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeOut(animationSpec = tweenAnimSpec) }, + ) +} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ChainPresets.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ChainPresets.kt similarity index 96% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ChainPresets.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/utils/ChainPresets.kt index 8803757fb..fa37446a6 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ChainPresets.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ChainPresets.kt @@ -1,6 +1,6 @@ -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils -import com.walletconnect.web3.modal.client.Modal +import com.reown.appkit.client.Modal internal fun Modal.Model.ChainImage.getImageData() = when(this) { is Modal.Model.ChainImage.Asset -> id diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/EthUtils.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/EthUtils.kt similarity index 92% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/EthUtils.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/utils/EthUtils.kt index c6ef624d3..61eab756e 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/EthUtils.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/EthUtils.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils object EthUtils { const val walletSwitchEthChain = "wallet_switchEthereumChain" diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/utils/Image.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/Image.kt new file mode 100644 index 000000000..8482abd44 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/Image.kt @@ -0,0 +1,16 @@ +package com.reown.appkit.utils + +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.ColorMatrix +import coil.request.ImageRequest +import com.reown.android.BuildConfig +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.wcKoinApp + +internal fun ImageRequest.Builder.imageHeaders() = apply { + addHeader("x-project-id", wcKoinApp.koin.get().value) + addHeader("x-sdk-version", BuildConfig.SDK_VERSION) + addHeader("x-sdk-type", "appkit") +} + +internal val grayColorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ParamsUtils.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ParamsUtils.kt similarity index 93% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ParamsUtils.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/utils/ParamsUtils.kt index 943cbd417..288994e5d 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ParamsUtils.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ParamsUtils.kt @@ -1,6 +1,6 @@ -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils -import com.walletconnect.web3.modal.client.Modal +import com.reown.appkit.client.Modal import org.intellij.lang.annotations.Language internal fun createAddEthChainParams(chain: Modal.Model.Chain): String { diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/SessionUtils.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/SessionUtils.kt similarity index 77% rename from product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/SessionUtils.kt rename to product/appkit/src/main/kotlin/com/reown/appkit/utils/SessionUtils.kt index 35db5ea72..95f2cc765 100644 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/SessionUtils.kt +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/SessionUtils.kt @@ -1,13 +1,13 @@ -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.sign.client.Sign -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.client.models.Account -import com.walletconnect.web3.modal.client.toModal -import com.walletconnect.web3.modal.domain.model.Session +import com.reown.android.internal.utils.CoreValidator +import com.reown.sign.client.Sign +import com.reown.util.Empty +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.models.Account +import com.reown.appkit.client.toModal +import com.reown.appkit.domain.model.Session internal fun String.toVisibleAddress() = "${take(4)}...${takeLast(4)}" @@ -31,7 +31,7 @@ internal fun Modal.Model.UpdatedSession.toSession(selectedChain: Modal.Model.Cha return Session.WalletConnect(getAddress(chain), chain.id, topic) } -internal fun String.toChain() = Web3Modal.chains.find { it.id == this } +internal fun String.toChain() = AppKit.chains.find { it.id == this } private fun Modal.Model.Session.getAccounts() = namespaces.values.toList().flatMap { it.accounts } @@ -52,8 +52,8 @@ internal fun Modal.Model.ApprovedSession.WalletConnectSession.getAddress(chain: ?.last() ?: String.Empty internal fun Session.getChains() = when(this) { - is Session.Coinbase -> Web3Modal.chains.filter { it.id == this.chain } - is Session.WalletConnect -> Web3Modal.getActiveSessionByTopic(topic)?.getChains() ?: Web3Modal.chains + is Session.Coinbase -> AppKit.chains.filter { it.id == this.chain } + is Session.WalletConnect -> AppKit.getActiveSessionByTopic(topic)?.getChains() ?: AppKit.chains } internal fun Session.toAccount() = Account(address, getChain(chain)) @@ -64,7 +64,7 @@ internal fun Sign.Model.Session.toAccount(session: Session.WalletConnect) = toMo Account(address, chain) } -internal fun getChain(chainId: String) = Web3Modal.chains.find { it.id == chainId } ?: Web3Modal.chains.first() +internal fun getChain(chainId: String) = AppKit.chains.find { it.id == chainId } ?: AppKit.chains.first() internal fun Session.toConnectorType() = when(this) { is Session.Coinbase -> Modal.ConnectorType.WALLET_CONNECT diff --git a/product/appkit/src/main/kotlin/com/reown/appkit/utils/ViewUtils.kt b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ViewUtils.kt new file mode 100644 index 000000000..bc83447b3 --- /dev/null +++ b/product/appkit/src/main/kotlin/com/reown/appkit/utils/ViewUtils.kt @@ -0,0 +1,14 @@ +package com.reown.appkit.utils + +import com.reown.appkit.ui.components.button.AccountButtonType +import com.reown.appkit.ui.components.button.ConnectButtonSize + +internal fun Int.toConnectButtonSize() = when (this) { + 1 -> ConnectButtonSize.SMALL + else -> ConnectButtonSize.NORMAL +} + +internal fun Int.toAccountButtonType() = when(this) { + 1 -> AccountButtonType.MIXED + else -> AccountButtonType.NORMAL +} diff --git a/product/web3modal/src/main/res/drawable-night/wallet_placeholder.xml b/product/appkit/src/main/res/drawable-night/wallet_placeholder.xml similarity index 100% rename from product/web3modal/src/main/res/drawable-night/wallet_placeholder.xml rename to product/appkit/src/main/res/drawable-night/wallet_placeholder.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/browser.xml b/product/appkit/src/main/res/drawable/browser.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/browser.xml rename to product/appkit/src/main/res/drawable/browser.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/dao.xml b/product/appkit/src/main/res/drawable/dao.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/dao.xml rename to product/appkit/src/main/res/drawable/dao.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/defi.xml b/product/appkit/src/main/res/drawable/defi.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/defi.xml rename to product/appkit/src/main/res/drawable/defi.xml diff --git a/product/web3modal/src/main/res/drawable/defi_alt.xml b/product/appkit/src/main/res/drawable/defi_alt.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/defi_alt.xml rename to product/appkit/src/main/res/drawable/defi_alt.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/eth.xml b/product/appkit/src/main/res/drawable/eth.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/eth.xml rename to product/appkit/src/main/res/drawable/eth.xml diff --git a/product/web3modal/src/main/res/drawable/ic_all_wallets.xml b/product/appkit/src/main/res/drawable/ic_all_wallets.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_all_wallets.xml rename to product/appkit/src/main/res/drawable/ic_all_wallets.xml diff --git a/product/web3modal/src/main/res/drawable/ic_browser.xml b/product/appkit/src/main/res/drawable/ic_browser.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_browser.xml rename to product/appkit/src/main/res/drawable/ic_browser.xml diff --git a/product/web3modal/src/main/res/drawable/ic_check.xml b/product/appkit/src/main/res/drawable/ic_check.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_check.xml rename to product/appkit/src/main/res/drawable/ic_check.xml diff --git a/product/web3modal/src/main/res/drawable/ic_chevron_left.xml b/product/appkit/src/main/res/drawable/ic_chevron_left.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_chevron_left.xml rename to product/appkit/src/main/res/drawable/ic_chevron_left.xml diff --git a/product/web3modal/src/main/res/drawable/ic_chevron_right.xml b/product/appkit/src/main/res/drawable/ic_chevron_right.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_chevron_right.xml rename to product/appkit/src/main/res/drawable/ic_chevron_right.xml diff --git a/product/web3modal/src/main/res/drawable/ic_close.xml b/product/appkit/src/main/res/drawable/ic_close.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_close.xml rename to product/appkit/src/main/res/drawable/ic_close.xml diff --git a/product/web3modal/src/main/res/drawable/ic_compass.xml b/product/appkit/src/main/res/drawable/ic_compass.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_compass.xml rename to product/appkit/src/main/res/drawable/ic_compass.xml diff --git a/product/web3modal/src/main/res/drawable/ic_copy.xml b/product/appkit/src/main/res/drawable/ic_copy.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_copy.xml rename to product/appkit/src/main/res/drawable/ic_copy.xml diff --git a/product/web3modal/src/main/res/drawable/ic_disconnect.xml b/product/appkit/src/main/res/drawable/ic_disconnect.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_disconnect.xml rename to product/appkit/src/main/res/drawable/ic_disconnect.xml diff --git a/product/web3modal/src/main/res/drawable/ic_email.xml b/product/appkit/src/main/res/drawable/ic_email.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_email.xml rename to product/appkit/src/main/res/drawable/ic_email.xml diff --git a/product/web3modal/src/main/res/drawable/ic_error.xml b/product/appkit/src/main/res/drawable/ic_error.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_error.xml rename to product/appkit/src/main/res/drawable/ic_error.xml diff --git a/product/web3modal/src/main/res/drawable/ic_external_link.xml b/product/appkit/src/main/res/drawable/ic_external_link.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_external_link.xml rename to product/appkit/src/main/res/drawable/ic_external_link.xml diff --git a/product/web3modal/src/main/res/drawable/ic_forward_chevron.xml b/product/appkit/src/main/res/drawable/ic_forward_chevron.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_forward_chevron.xml rename to product/appkit/src/main/res/drawable/ic_forward_chevron.xml diff --git a/product/web3modal/src/main/res/drawable/ic_google_playstore.xml b/product/appkit/src/main/res/drawable/ic_google_playstore.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_google_playstore.xml rename to product/appkit/src/main/res/drawable/ic_google_playstore.xml diff --git a/product/web3modal/src/main/res/drawable/ic_info.xml b/product/appkit/src/main/res/drawable/ic_info.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_info.xml rename to product/appkit/src/main/res/drawable/ic_info.xml diff --git a/product/web3modal/src/main/res/drawable/ic_mobile.xml b/product/appkit/src/main/res/drawable/ic_mobile.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_mobile.xml rename to product/appkit/src/main/res/drawable/ic_mobile.xml diff --git a/product/web3modal/src/main/res/drawable/ic_question_mark.xml b/product/appkit/src/main/res/drawable/ic_question_mark.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_question_mark.xml rename to product/appkit/src/main/res/drawable/ic_question_mark.xml diff --git a/product/web3modal/src/main/res/drawable/ic_retry.xml b/product/appkit/src/main/res/drawable/ic_retry.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_retry.xml rename to product/appkit/src/main/res/drawable/ic_retry.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_scan.xml b/product/appkit/src/main/res/drawable/ic_scan.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/ic_scan.xml rename to product/appkit/src/main/res/drawable/ic_scan.xml diff --git a/product/web3modal/src/main/res/drawable/ic_scan_qr.xml b/product/appkit/src/main/res/drawable/ic_scan_qr.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_scan_qr.xml rename to product/appkit/src/main/res/drawable/ic_scan_qr.xml diff --git a/product/web3modal/src/main/res/drawable/ic_search.xml b/product/appkit/src/main/res/drawable/ic_search.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_search.xml rename to product/appkit/src/main/res/drawable/ic_search.xml diff --git a/product/web3modal/src/main/res/drawable/ic_select_network.xml b/product/appkit/src/main/res/drawable/ic_select_network.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_select_network.xml rename to product/appkit/src/main/res/drawable/ic_select_network.xml diff --git a/product/web3modal/src/main/res/drawable/ic_success.xml b/product/appkit/src/main/res/drawable/ic_success.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_success.xml rename to product/appkit/src/main/res/drawable/ic_success.xml diff --git a/product/web3modal/src/main/res/drawable/ic_swap.xml b/product/appkit/src/main/res/drawable/ic_swap.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_swap.xml rename to product/appkit/src/main/res/drawable/ic_swap.xml diff --git a/product/web3modal/src/main/res/drawable/ic_wallet.xml b/product/appkit/src/main/res/drawable/ic_wallet.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_wallet.xml rename to product/appkit/src/main/res/drawable/ic_wallet.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_wallet_connect_logo.xml b/product/appkit/src/main/res/drawable/ic_wallet_connect_logo.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/ic_wallet_connect_logo.xml rename to product/appkit/src/main/res/drawable/ic_wallet_connect_logo.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_wallet_connect_qr_logo.xml b/product/appkit/src/main/res/drawable/ic_wallet_connect_qr_logo.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/ic_wallet_connect_qr_logo.xml rename to product/appkit/src/main/res/drawable/ic_wallet_connect_qr_logo.xml diff --git a/product/web3modal/src/main/res/drawable/ic_web.xml b/product/appkit/src/main/res/drawable/ic_web.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/ic_web.xml rename to product/appkit/src/main/res/drawable/ic_web.xml diff --git a/product/web3modal/src/main/res/drawable/layers.xml b/product/appkit/src/main/res/drawable/layers.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/layers.xml rename to product/appkit/src/main/res/drawable/layers.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/lock.xml b/product/appkit/src/main/res/drawable/lock.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/lock.xml rename to product/appkit/src/main/res/drawable/lock.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/login.xml b/product/appkit/src/main/res/drawable/login.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/login.xml rename to product/appkit/src/main/res/drawable/login.xml diff --git a/product/web3modal/src/main/res/drawable/network.xml b/product/appkit/src/main/res/drawable/network.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/network.xml rename to product/appkit/src/main/res/drawable/network.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/nft.xml b/product/appkit/src/main/res/drawable/nft.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/nft.xml rename to product/appkit/src/main/res/drawable/nft.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/noun.xml b/product/appkit/src/main/res/drawable/noun.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/noun.xml rename to product/appkit/src/main/res/drawable/noun.xml diff --git a/product/walletconnectmodal/src/main/res/drawable/profile.xml b/product/appkit/src/main/res/drawable/profile.xml similarity index 100% rename from product/walletconnectmodal/src/main/res/drawable/profile.xml rename to product/appkit/src/main/res/drawable/profile.xml diff --git a/product/web3modal/src/main/res/drawable/system.xml b/product/appkit/src/main/res/drawable/system.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/system.xml rename to product/appkit/src/main/res/drawable/system.xml diff --git a/product/web3modal/src/main/res/drawable/wallet_placeholder.xml b/product/appkit/src/main/res/drawable/wallet_placeholder.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/wallet_placeholder.xml rename to product/appkit/src/main/res/drawable/wallet_placeholder.xml diff --git a/product/web3modal/src/main/res/drawable/walletconnect_blue.xml b/product/appkit/src/main/res/drawable/walletconnect_blue.xml similarity index 100% rename from product/web3modal/src/main/res/drawable/walletconnect_blue.xml rename to product/appkit/src/main/res/drawable/walletconnect_blue.xml diff --git a/product/web3modal/src/main/res/layout/view_button.xml b/product/appkit/src/main/res/layout/view_button.xml similarity index 100% rename from product/web3modal/src/main/res/layout/view_button.xml rename to product/appkit/src/main/res/layout/view_button.xml diff --git a/product/web3modal/src/main/res/layout/view_web3modal.xml b/product/appkit/src/main/res/layout/view_web3modal.xml similarity index 100% rename from product/web3modal/src/main/res/layout/view_web3modal.xml rename to product/appkit/src/main/res/layout/view_web3modal.xml diff --git a/product/web3modal/src/main/res/navigation/web3modal_graph.xml b/product/appkit/src/main/res/navigation/web3modal_graph.xml similarity index 81% rename from product/web3modal/src/main/res/navigation/web3modal_graph.xml rename to product/appkit/src/main/res/navigation/web3modal_graph.xml index f2369aaf5..92a84e415 100644 --- a/product/web3modal/src/main/res/navigation/web3modal_graph.xml +++ b/product/appkit/src/main/res/navigation/web3modal_graph.xml @@ -6,6 +6,6 @@ + android:name="com.reown.appkit.ui.AppKitSheet" /> diff --git a/product/web3modal/src/main/res/values/attrs.xml b/product/appkit/src/main/res/values/attrs.xml similarity index 100% rename from product/web3modal/src/main/res/values/attrs.xml rename to product/appkit/src/main/res/values/attrs.xml diff --git a/product/web3modal/src/main/res/values/theme.xml b/product/appkit/src/main/res/values/theme.xml similarity index 100% rename from product/web3modal/src/main/res/values/theme.xml rename to product/appkit/src/main/res/values/theme.xml diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/buttons/ButtonsTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/components/buttons/ButtonsTest.kt similarity index 78% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/buttons/ButtonsTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/components/buttons/ButtonsTest.kt index f361f70f7..d5f8e1b9e 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/buttons/ButtonsTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/components/buttons/ButtonsTest.kt @@ -1,12 +1,12 @@ -package com.walletconnect.web3.modal.ui.components.buttons +package com.reown.appkit.ui.components.buttons import com.android.resources.NightMode -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.presets.Web3ModalChainsPresets -import com.walletconnect.web3.modal.ui.components.button.AccountButtonState -import com.walletconnect.web3.modal.ui.components.button.ConnectButton -import com.walletconnect.web3.modal.ui.components.button.ConnectButtonSize -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.appkit.client.Modal +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.ui.components.button.AccountButtonState +import com.reown.appkit.ui.components.button.ConnectButton +import com.reown.appkit.ui.components.button.ConnectButtonSize +import com.reown.appkit.utils.ScreenShotTest import org.junit.Test internal class ButtonsTest : ScreenShotTest("component/button") { @@ -33,7 +33,7 @@ internal class ButtonsTest : ScreenShotTest("component/button") { @Test fun `test AccountButton in LightMode`() = runComponentScreenShotTest { - val chain = Web3ModalChainsPresets.ethChains["1"]!! + val chain = AppKitChainsPresets.ethChains["1"]!! AccountButtonState(AccountButtonState.Invalid) {} AccountButtonState(AccountButtonState.Loading) {} AccountButtonState(AccountButtonState.Normal("0x2765d421FB91182490D602E671a")) {} @@ -42,7 +42,7 @@ internal class ButtonsTest : ScreenShotTest("component/button") { @Test fun `test AccountButton in DarkMode`() = runComponentScreenShotTest(nightMode = NightMode.NIGHT) { - val chain = Web3ModalChainsPresets.ethChains["1"]!! + val chain = AppKitChainsPresets.ethChains["1"]!! AccountButtonState(AccountButtonState.Invalid) {} AccountButtonState(AccountButtonState.Loading) {} AccountButtonState(AccountButtonState.Normal("0x2765d421FB91182490D602E671a")) {} diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/internal/ModalSnackBarTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/components/internal/ModalSnackBarTest.kt similarity index 75% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/internal/ModalSnackBarTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/components/internal/ModalSnackBarTest.kt index f32b87ef5..b045db4af 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/components/internal/ModalSnackBarTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/components/internal/ModalSnackBarTest.kt @@ -1,11 +1,11 @@ -package com.walletconnect.web3.modal.ui.components.internal +package com.reown.appkit.ui.components.internal import com.android.resources.NightMode -import com.walletconnect.web3.modal.ui.components.internal.snackbar.ModalSnackBar -import com.walletconnect.web3.modal.ui.components.internal.snackbar.SnackBarEvent -import com.walletconnect.web3.modal.ui.components.internal.snackbar.SnackBarEventType -import com.walletconnect.web3.modal.ui.components.internal.snackbar.SnackbarDuration -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.appkit.ui.components.internal.snackbar.ModalSnackBar +import com.reown.appkit.ui.components.internal.snackbar.SnackBarEvent +import com.reown.appkit.ui.components.internal.snackbar.SnackBarEventType +import com.reown.appkit.ui.components.internal.snackbar.SnackbarDuration +import com.reown.appkit.utils.ScreenShotTest import org.junit.Test internal class ModalSnackBarTest : ScreenShotTest("component/internal/snackbar") { diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/AccountRouteTest.kt similarity index 78% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountRouteTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/AccountRouteTest.kt index 65c8e78fe..69c787e95 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountRouteTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/AccountRouteTest.kt @@ -1,19 +1,19 @@ -package com.walletconnect.web3.modal.ui.routes.account +package com.reown.appkit.ui.routes.account import androidx.navigation.NavController import com.android.resources.NightMode import com.android.resources.ScreenOrientation -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.model.AccountData -import com.walletconnect.web3.modal.domain.model.Balance -import com.walletconnect.web3.modal.domain.usecase.GetSelectedChainUseCase -import com.walletconnect.web3.modal.presets.Web3ModalChainsPresets -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.account.account.AccountRoute -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.android.internal.common.wcKoinApp +import com.reown.modal.ui.model.UiState +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.model.AccountData +import com.reown.appkit.domain.model.Balance +import com.reown.appkit.domain.usecase.GetSelectedChainUseCase +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.account.account.AccountRoute +import com.reown.appkit.utils.ScreenShotTest import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.flow.StateFlow @@ -33,19 +33,19 @@ internal class AccountRouteTest: ScreenShotTest("account/${Route.ACCOUNT.path}") private val getSelectedChainUseCase: GetSelectedChainUseCase = mockk() private val accountData = AccountData( address = "0x2765d421FB91182490D602E671a", - chains = Web3ModalChainsPresets.ethChains.values.toList() + chains = AppKitChainsPresets.ethChains.values.toList() ) @Before fun setup() { - Web3Modal.chains = Web3ModalChainsPresets.ethChains.values.toList() + AppKit.chains = AppKitChainsPresets.ethChains.values.toList() every { viewModel.accountState } returns uiState every { viewModel.selectedChain } returns selectedChain every { viewModel.balanceState } returns balance - every { selectedChain.value } returns Web3ModalChainsPresets.ethChains["1"]!! - every { balance.value } returns Balance(Web3ModalChainsPresets.ethToken, "0x0") + every { selectedChain.value } returns AppKitChainsPresets.ethChains["1"]!! + every { balance.value } returns Balance(AppKitChainsPresets.ethToken, "0x0") every { getSelectedChainUseCase() } returns "1" - every { viewModel.getSelectedChainOrFirst() } returns Web3ModalChainsPresets.ethChains.getOrElse("1") { throw IllegalStateException("Chain not found") } + every { viewModel.getSelectedChainOrFirst() } returns AppKitChainsPresets.ethChains.getOrElse("1") { throw IllegalStateException("Chain not found") } val modules = listOf( module { single { getSelectedChainUseCase } } diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChainSwitchRedirectTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChainSwitchRedirectTest.kt new file mode 100644 index 000000000..6c6918a6e --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChainSwitchRedirectTest.kt @@ -0,0 +1,46 @@ +package com.reown.appkit.ui.routes.account + +import com.android.resources.NightMode +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.account.chain_redirect.ChainRedirectState +import com.reown.appkit.ui.routes.account.chain_redirect.ChainSwitchRedirectScreen +import com.reown.appkit.utils.ScreenShotTest +import org.junit.Ignore +import org.junit.Test + +@Ignore("This test is not working on CI for Sonar only") +internal class ChainSwitchRedirectTest : ScreenShotTest("account/" + Route.CHAIN_SWITCH_REDIRECT.path) { + + private val chain = AppKitChainsPresets.ethChains["1"]!! + + @Test + fun `test ChainSwitchRedirect with Loading in LightMode`() = runRouteScreenShotTest( + title = chain.chainName + ) { + ChainSwitchRedirectScreen(chain, ChainRedirectState.Loading, {}) + } + + @Test + fun `test ChainSwitchRedirect with Loading in DarkMode`() = runRouteScreenShotTest( + title = chain.chainName, + nightMode = NightMode.NIGHT + ) { + ChainSwitchRedirectScreen(chain, ChainRedirectState.Loading, {}) + } + + @Test + fun `test ChainSwitchRedirect with Decline in LightMode`() = runRouteScreenShotTest( + title = chain.chainName + ) { + ChainSwitchRedirectScreen(chain, ChainRedirectState.Declined, {}) + } + + @Test + fun `test ChainSwitchRedirect with Decline in DarkMode`() = runRouteScreenShotTest( + title = chain.chainName, + nightMode = NightMode.NIGHT + ) { + ChainSwitchRedirectScreen(chain, ChainRedirectState.Declined, {}) + } +} \ No newline at end of file diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChangeNetworkRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChangeNetworkRouteTest.kt new file mode 100644 index 000000000..3f75e176b --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/ChangeNetworkRouteTest.kt @@ -0,0 +1,66 @@ +package com.reown.appkit.ui.routes.account + +import com.android.resources.NightMode +import com.android.resources.ScreenOrientation +import com.reown.android.internal.common.wcKoinApp +import com.reown.modal.ui.model.UiState +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.domain.model.AccountData +import com.reown.appkit.domain.usecase.GetSelectedChainUseCase +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.account.change_network.ChangeNetworkRoute +import com.reown.appkit.utils.ScreenShotTest +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.StateFlow +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +import org.koin.dsl.module + +@Ignore("This test is not working on CI for Sonar only") +internal class ChangeNetworkRouteTest: ScreenShotTest("account/${Route.CHANGE_NETWORK.path}") { + + private val viewModel: AccountViewModel = mockk() + private val selectedChain: StateFlow = mockk() + private val uiState: StateFlow> = mockk() + private val getSelectedChainUseCase: GetSelectedChainUseCase = mockk() + + @Before + fun setup() { + AppKit.chains = AppKitChainsPresets.ethChains.values.toList() + every { viewModel.accountState } returns uiState + every { uiState.value } returns UiState.Success(AccountData("0x2765d421FB91182490D602E671a", AppKitChainsPresets.ethChains.values.toList())) + every { viewModel.selectedChain } returns selectedChain + every { selectedChain.value } returns AppKitChainsPresets.ethChains["1"]!! + every { getSelectedChainUseCase() } returns "1" + every { viewModel.getSelectedChainOrFirst() } returns AppKitChainsPresets.ethChains.getOrElse("1") { throw IllegalStateException("Chain not found") } + wcKoinApp.koin.loadModules(modules = listOf(module { single { getSelectedChainUseCase } })) + + } + + @Test + fun `test ChangeNetworkRoute in LightMode`() = runRouteScreenShotTest( + title = Route.CHANGE_NETWORK.title + ) { + ChangeNetworkRoute(accountViewModel = viewModel) + } + + @Test + fun `test ChangeNetworkRoute in DarkMode`() = runRouteScreenShotTest( + title = Route.CHANGE_NETWORK.title, + nightMode = NightMode.NIGHT + ) { + ChangeNetworkRoute(accountViewModel = viewModel) + } + + @Test + fun `test ChangeNetworkRoute in Landscape`() = runRouteScreenShotTest( + title = Route.CHANGE_NETWORK.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + ChangeNetworkRoute(accountViewModel = viewModel) + } +} \ No newline at end of file diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/WhatIsNetworkRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/WhatIsNetworkRouteTest.kt similarity index 75% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/WhatIsNetworkRouteTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/WhatIsNetworkRouteTest.kt index 559fa6923..5dd86ce39 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/WhatIsNetworkRouteTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/account/WhatIsNetworkRouteTest.kt @@ -1,11 +1,11 @@ -package com.walletconnect.web3.modal.ui.routes.account +package com.reown.appkit.ui.routes.account import com.android.resources.NightMode import com.android.resources.ScreenOrientation -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.account.what_is_network.WhatIsNetworkRoute -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.account.what_is_network.WhatIsNetworkRoute +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/AllWalletsRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/AllWalletsRouteTest.kt new file mode 100644 index 000000000..85a35d21b --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/AllWalletsRouteTest.kt @@ -0,0 +1,167 @@ +package com.reown.appkit.ui.routes.connect + +import com.android.resources.NightMode +import com.android.resources.ScreenOrientation +import com.reown.modal.ui.model.LoadingState +import com.reown.util.Empty +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.all_wallets.AllWalletsRoute +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.StateFlow +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("This test is not working on CI for Sonar only") +internal class AllWalletsRouteTest : ScreenShotTest("connect/${Route.ALL_WALLETS.path}") { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val viewModel: ConnectViewModel = mockk() + private val walletsState: StateFlow = mockk() + + @Before + fun setup() { + every { viewModel.walletsState } returns walletsState + every { viewModel.searchPhrase } returns String.Empty + } + + @Test + fun `test AllWalletsRoute with LoadingRefresh in LightMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title + ) { + every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with LoadingRefresh in DarkMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + nightMode = NightMode.NIGHT + ) { + every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with LoadingRefresh in Landscape`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with EmptyList in LightMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title + ) { + every { viewModel.walletsState.value } returns WalletsData() + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with EmptyList in DarkMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + nightMode = NightMode.NIGHT + ) { + every { viewModel.walletsState.value } returns WalletsData() + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with EmptyList in Landscape`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + every { viewModel.walletsState.value } returns WalletsData() + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loaded Wallets in LightMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loaded Wallets in DarkMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + nightMode = NightMode.NIGHT + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loaded Wallets in Landscape`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loading Append in LightMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loading Append in DarkMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + nightMode = NightMode.NIGHT + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Loading Append in Landscape`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Search in LightMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title + ) { + every { viewModel.searchPhrase } returns "Meta" + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Search in DarkMode`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + nightMode = NightMode.NIGHT + ) { + every { viewModel.searchPhrase } returns "Meta" + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) + AllWalletsRoute(connectViewModel = viewModel) + } + + @Test + fun `test AllWalletsRoute with Search in Landscape`() = runRouteScreenShotTest( + title = Route.ALL_WALLETS.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + every { viewModel.searchPhrase } returns "Meta" + every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) + AllWalletsRoute(connectViewModel = viewModel) + } +} \ No newline at end of file diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ConnectYourWalletRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ConnectYourWalletRouteTest.kt new file mode 100644 index 000000000..58269c1e8 --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ConnectYourWalletRouteTest.kt @@ -0,0 +1,85 @@ +package com.reown.appkit.ui.routes.connect + +import com.android.resources.NightMode +import com.reown.android.internal.common.modal.data.model.Wallet +import com.reown.modal.ui.model.UiState +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.connect_wallet.ConnectWalletRoute +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.StateFlow +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("This test is not working on CI for Sonar only") +internal class ConnectYourWalletRouteTest : ScreenShotTest("connect/${Route.CONNECT_YOUR_WALLET.path}") { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val viewModel: ConnectViewModel = mockk() + private val uiState: StateFlow>> = mockk() + + @Before + fun setup() { + every { viewModel.uiState } returns uiState + } + + @Test + fun `test ConnectYourWallet with Loading UiState in LightMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title + ) { + every { uiState.value } returns UiState.Loading() + ConnectWalletRoute(connectViewModel = viewModel) + } + + @Test + fun `test ConnectYourWallet with Loading UiState in DarkMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title, + nightMode = NightMode.NIGHT + ) { + every { uiState.value } returns UiState.Loading() + ConnectWalletRoute(connectViewModel = viewModel) + } + + @Test + fun `test ConnectYourWallet with Error UiState in LightMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title + ) { + every { uiState.value } returns UiState.Error(Throwable("Connection error")) + ConnectWalletRoute(connectViewModel = viewModel) + } + + @Test + fun `test ConnectYourWallet with Error UiState in DarkMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title, + nightMode = NightMode.NIGHT + ) { + every { uiState.value } returns UiState.Error(Throwable("Connection error")) + ConnectWalletRoute(connectViewModel = viewModel) + } + + @Test + fun `test ConnectYourWallet with Success UiState in LightMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title + ) { + every { uiState.value } returns UiState.Success(testWallets) + every { viewModel.getWalletsTotalCount() } returns testWallets.size + ConnectWalletRoute(connectViewModel = viewModel) + } + + @Test + fun `test ConnectYourWallet with Success UiState in DarkMode`() = runRouteScreenShotTest( + title = Route.CONNECT_YOUR_WALLET.title, + nightMode = NightMode.NIGHT + ) { + every { uiState.value } returns UiState.Success(testWallets) + every { viewModel.getWalletsTotalCount() } returns testWallets.size + ConnectWalletRoute(connectViewModel = viewModel) + } +} \ No newline at end of file diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/GetAWalletRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/GetAWalletRouteTest.kt new file mode 100644 index 000000000..a49aa8da2 --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/GetAWalletRouteTest.kt @@ -0,0 +1,42 @@ +package com.reown.appkit.ui.routes.connect + +import com.android.resources.NightMode +import com.android.resources.ScreenOrientation +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.get_wallet.GetAWalletRoute +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("This test is not working on CI for Sonar only") +internal class GetAWalletRouteTest: ScreenShotTest("connect/${Route.GET_A_WALLET.path}") { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + @Test + fun `test GetAWalletRoute in LightMode`() = runRouteScreenShotTest( + title = Route.GET_A_WALLET.title + ) { + GetAWalletRoute(testWallets) + } + + @Test + fun `test GetAWalletRoute in DarkMode`() = runRouteScreenShotTest( + title = Route.GET_A_WALLET.title, + nightMode = NightMode.NIGHT + ) { + GetAWalletRoute(testWallets) + } + + @Test + fun `test GetAWalletRoute in Landscape`() = runRouteScreenShotTest( + title = Route.GET_A_WALLET.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + GetAWalletRoute(testWallets) + } +} diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/RedirectWalletRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/RedirectWalletRouteTest.kt similarity index 86% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/RedirectWalletRouteTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/RedirectWalletRouteTest.kt index 5d35e136d..8c47be874 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/RedirectWalletRouteTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/RedirectWalletRouteTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.web3.modal.ui.routes.connect +package com.reown.appkit.ui.routes.connect import com.android.resources.NightMode import com.android.resources.ScreenOrientation -import com.walletconnect.web3.modal.ui.components.internal.commons.switch.PlatformTab -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.redirect.RedirectState -import com.walletconnect.web3.modal.ui.routes.connect.redirect.RedirectWalletScreen -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.appkit.ui.components.internal.commons.switch.PlatformTab +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.previews.testWallets +import com.reown.appkit.ui.routes.connect.redirect.RedirectState +import com.reown.appkit.ui.routes.connect.redirect.RedirectWalletScreen +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest import org.junit.Ignore import org.junit.Rule import org.junit.Test diff --git a/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ScanCodeRouteTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ScanCodeRouteTest.kt new file mode 100644 index 000000000..db8c45350 --- /dev/null +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/ScanCodeRouteTest.kt @@ -0,0 +1,51 @@ +package com.reown.appkit.ui.routes.connect + +import com.android.resources.NightMode +import com.android.resources.ScreenOrientation +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.connect.scan_code.ScanQRCodeRoute +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest +import io.mockk.every +import io.mockk.mockk +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore("This test is not working on CI for Sonar only") +internal class ScanCodeRouteTest: ScreenShotTest("connect/${Route.QR_CODE.path}") { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val viewModel: ConnectViewModel = mockk() + + @Before + fun setup() { + every { viewModel.uri } returns "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + } + + @Test + fun `test ScanCodeRoute in LightMode`() = runRouteScreenShotTest( + title = Route.QR_CODE.title + ) { + ScanQRCodeRoute(connectViewModel = viewModel) + } + + @Test + fun `test ScanCodeRoute in DarkMode`() = runRouteScreenShotTest( + title = Route.QR_CODE.title, + nightMode = NightMode.NIGHT + ) { + ScanQRCodeRoute(connectViewModel = viewModel) + } + + @Test + fun `test ScanCodeRoute in Landscape`() = runRouteScreenShotTest( + title = Route.QR_CODE.title, + orientation = ScreenOrientation.LANDSCAPE + ) { + ScanQRCodeRoute(connectViewModel = viewModel) + } +} diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WhatIsWalletTest.kt b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/WhatIsWalletTest.kt similarity index 77% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WhatIsWalletTest.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/WhatIsWalletTest.kt index 65b240dd0..f938ec371 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/WhatIsWalletTest.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/ui/routes/connect/WhatIsWalletTest.kt @@ -1,12 +1,12 @@ -package com.walletconnect.web3.modal.ui.routes.connect +package com.reown.appkit.ui.routes.connect import androidx.navigation.NavController import com.android.resources.NightMode import com.android.resources.ScreenOrientation -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.connect.what_is_wallet.WhatIsWallet -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest +import com.reown.appkit.ui.navigation.Route +import com.reown.appkit.ui.routes.connect.what_is_wallet.WhatIsWallet +import com.reown.appkit.utils.MainDispatcherRule +import com.reown.appkit.utils.ScreenShotTest import io.mockk.mockk import org.junit.Ignore import org.junit.Rule diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/Dispatchers.kt b/product/appkit/src/test/kotlin/com/reown/appkit/utils/Dispatchers.kt similarity index 94% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/Dispatchers.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/utils/Dispatchers.kt index 6f188e5f2..bb20d1288 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/Dispatchers.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/utils/Dispatchers.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/PaparazziUtilts.kt b/product/appkit/src/test/kotlin/com/reown/appkit/utils/PaparazziUtilts.kt similarity index 82% rename from product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/PaparazziUtilts.kt rename to product/appkit/src/test/kotlin/com/reown/appkit/utils/PaparazziUtilts.kt index 44f340ff2..175d76e51 100644 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/utils/PaparazziUtilts.kt +++ b/product/appkit/src/test/kotlin/com/reown/appkit/utils/PaparazziUtilts.kt @@ -1,4 +1,4 @@ -package com.walletconnect.web3.modal.utils +package com.reown.appkit.utils import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -23,13 +23,13 @@ import com.android.ide.common.rendering.api.SessionParams import com.android.resources.KeyboardState import com.android.resources.NightMode import com.android.resources.ScreenOrientation -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.web3.modal.ui.components.internal.root.Web3ModalRoot -import com.walletconnect.web3.modal.ui.components.internal.root.rememberWeb3ModalRootState -import com.walletconnect.web3.modal.ui.components.internal.snackbar.rememberSnackBarState -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.wcKoinApp +import com.reown.appkit.ui.components.internal.root.AppKitRoot +import com.reown.appkit.ui.components.internal.root.rememberAppKitRootState +import com.reown.appkit.ui.components.internal.snackbar.rememberSnackBarState +import com.reown.appkit.ui.theme.ProvideAppKitThemeComposition +import com.reown.appkit.ui.theme.AppKitTheme import org.junit.Rule import org.koin.dsl.module import java.io.File @@ -82,14 +82,14 @@ internal abstract class ScreenShotTest( ) snapshot { val scope = rememberCoroutineScope() - val rootState = rememberWeb3ModalRootState(coroutineScope = scope, navController = rememberNavController()) + val rootState = rememberAppKitRootState(coroutineScope = scope, navController = rememberNavController()) val snackBarState = rememberSnackBarState(coroutineScope = scope) - ProvideWeb3ModalThemeComposition { + ProvideAppKitThemeComposition { Surface( color = Color.Transparent, shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp) ) { - Web3ModalRoot( + AppKitRoot( rootState = rootState, snackBarState = snackBarState, closeModal = {}, @@ -113,8 +113,8 @@ internal abstract class ScreenShotTest( DeviceConfig(nightMode = nightMode, orientation = orientation, keyboardState = keyboardState) ) snapshot { - ProvideWeb3ModalThemeComposition { - Box(modifier = Modifier.background(Web3ModalTheme.colors.background.color100)) { + ProvideAppKitThemeComposition { + Box(modifier = Modifier.background(AppKitTheme.colors.background.color100)) { Column(modifier = Modifier.padding(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp)) { content() } diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in DarkMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in DarkMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in Landscape.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in Landscape.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in Landscape.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in LightMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in LightMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Error UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in DarkMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in DarkMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in Landscape.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in Landscape.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in Landscape.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in LightMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in LightMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Loading UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in DarkMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in DarkMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in Landscape.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in Landscape.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in Landscape.png diff --git a/product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in LightMode.png b/product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in LightMode.png rename to product/appkit/src/test/snapshots/account/account/images/com.walletconnect.web3.modal.ui.routes.account_AccountRouteTest_test AccountRoute with Success UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in DarkMode.png b/product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in DarkMode.png rename to product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in LightMode.png b/product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in LightMode.png rename to product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Decline in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in DarkMode.png b/product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in DarkMode.png rename to product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in LightMode.png b/product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in LightMode.png rename to product/appkit/src/test/snapshots/account/chain_switch_redirect/images/com.walletconnect.web3.modal.ui.routes.account_ChainSwitchRedirectTest_test ChainSwitchRedirect with Loading in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in DarkMode.png b/product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in DarkMode.png rename to product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in Landscape.png b/product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in Landscape.png rename to product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in Landscape.png diff --git a/product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in LightMode.png b/product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in LightMode.png rename to product/appkit/src/test/snapshots/account/change_network/images/com.walletconnect.web3.modal.ui.routes.account_ChangeNetworkRouteTest_test ChangeNetworkRoute in LightMode.png diff --git a/product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in DarkMode.png b/product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in DarkMode.png rename to product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in Landscape.png b/product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in Landscape.png rename to product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in Landscape.png diff --git a/product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in LightMode.png b/product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in LightMode.png rename to product/appkit/src/test/snapshots/account/what_is_network/images/com.walletconnect.web3.modal.ui.routes.account_WhatIsNetworkRouteTest_test WhatIsWallet in LightMode.png diff --git a/product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in DarkMode.png b/product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in DarkMode.png rename to product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in LightMode.png b/product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in LightMode.png rename to product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test AccountButton in LightMode.png diff --git a/product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in DarkMode.png b/product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in DarkMode.png rename to product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in LightMode.png b/product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in LightMode.png rename to product/appkit/src/test/snapshots/component/button/images/com.walletconnect.web3.modal.ui.components.buttons_ButtonsTest_test ConnectButton in LightMode.png diff --git a/product/web3modal/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in DarkMode.png b/product/appkit/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in DarkMode.png rename to product/appkit/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in LightMode.png b/product/appkit/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in LightMode.png rename to product/appkit/src/test/snapshots/component/internal/snackbar/images/com.walletconnect.web3.modal.ui.components.internal_ModalSnackBarTest_test ModalSnackBar in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in DarkMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in DarkMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in Landscape.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in Landscape.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in LightMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in LightMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with EmptyList in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in DarkMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in DarkMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in Landscape.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in Landscape.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in LightMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in LightMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loaded Wallets in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in DarkMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in DarkMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in Landscape.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in Landscape.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in LightMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in LightMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Loading Append in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in DarkMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in DarkMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in Landscape.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in Landscape.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in LightMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in LightMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with LoadingRefresh in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in DarkMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in DarkMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in Landscape.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in Landscape.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in LightMode.png b/product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in LightMode.png rename to product/appkit/src/test/snapshots/connect/all_wallets/images/com.walletconnect.web3.modal.ui.routes.connect_AllWalletsRouteTest_test AllWalletsRoute with Search in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in DarkMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in DarkMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in LightMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in LightMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Error UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in DarkMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in DarkMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in LightMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in LightMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Loading UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in DarkMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in DarkMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in LightMode.png b/product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in LightMode.png rename to product/appkit/src/test/snapshots/connect/connect_your_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_ConnectYourWalletRouteTest_test ConnectYourWallet with Success UiState in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in DarkMode.png b/product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in DarkMode.png rename to product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in Landscape.png b/product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in Landscape.png rename to product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in LightMode.png b/product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in LightMode.png rename to product/appkit/src/test/snapshots/connect/get_a_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_GetAWalletRouteTest_test GetAWalletRoute in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in DarkMode.png b/product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in DarkMode.png rename to product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in Landscape.png b/product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in Landscape.png rename to product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in LightMode.png b/product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in LightMode.png rename to product/appkit/src/test/snapshots/connect/qr_code/images/com.walletconnect.web3.modal.ui.routes.connect_ScanCodeRouteTest_test ScanCodeRoute in LightMode.png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in DarkMode .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in DarkMode .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in DarkMode .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in DarkMode .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in Landscape .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in Landscape .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in Landscape .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in Landscape .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in LightMode .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in LightMode .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in LightMode .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Loading state in LightMode .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in DarkMode .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in DarkMode .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in DarkMode .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in DarkMode .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in Landscape .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in Landscape .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in Landscape .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with NotDetected state in Landscape .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in DarkMode .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in DarkMode .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in DarkMode .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in DarkMode .png diff --git a/product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in Landscape .png b/product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in Landscape .png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in Landscape .png rename to product/appkit/src/test/snapshots/connect/redirect/images/com.walletconnect.web3.modal.ui.routes.connect_RedirectWalletRouteTest_test RedirectWalletRoute with Reject state in Landscape .png diff --git a/product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in DarkMode.png b/product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in DarkMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in DarkMode.png rename to product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in DarkMode.png diff --git a/product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in Landscape.png b/product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in Landscape.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in Landscape.png rename to product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in Landscape.png diff --git a/product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in LightMode.png b/product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in LightMode.png similarity index 100% rename from product/web3modal/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in LightMode.png rename to product/appkit/src/test/snapshots/connect/what_is_wallet/images/com.walletconnect.web3.modal.ui.routes.connect_WhatIsWalletTest_test WhatIsWallet in LightMode.png diff --git a/product/walletconnectmodal/ReadMe.md b/product/walletconnectmodal/ReadMe.md deleted file mode 100644 index 52c16f0f6..000000000 --- a/product/walletconnectmodal/ReadMe.md +++ /dev/null @@ -1,44 +0,0 @@ -# **WalletConnect Modal - Kotlin** - -# Installation - -:::caution -**The WalletConnectModal SDK is currently in Alpha and is not production-ready**. - -It's public API and associated documentation may still see significant and breaking changes. -::: - -Kotlin implementation of WalletConnectModal for Android applications. - -Android Core ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/android-core) - -WalletConnectModal ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/walletconnect-modal) - -## Requirements - -* Android min SDK 23 -* Java 11 - -## Documentation and usage -* [WalletConnectModal installation](https://docs.walletconnect.com/2.0/android/walletconnectmodal/installation) -* [WalletConnectModal usage](https://docs.walletconnect.com/2.0/android/walletconnectmodal/usage) - -## Installation -root/build.gradle.kts: -```gradle -allprojects { - repositories { - mavenCentral() - maven { url "https://jitpack.io" } - } -} -``` - -app/build.gradle.kts - -```gradle -implementation(platform("com.walletconnect:android-bom:$BOM_VERSION")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:walletconnect-modal") -``` - diff --git a/product/walletconnectmodal/build.gradle.kts b/product/walletconnectmodal/build.gradle.kts deleted file mode 100644 index 0ac53fef6..000000000 --- a/product/walletconnectmodal/build.gradle.kts +++ /dev/null @@ -1,99 +0,0 @@ -plugins { - id("com.android.library") - id(libs.plugins.kotlin.android.get().pluginId) - alias(libs.plugins.google.ksp) - alias(libs.plugins.paparazzi) - id("publish-module-android") - id("jacoco-report") -} - -project.apply { - extra[KEY_PUBLISH_ARTIFACT_ID] = WC_MODAL - extra[KEY_PUBLISH_VERSION] = WC_MODAL_VERSION - extra[KEY_SDK_NAME] = "Wallet Connect Modal" -} - -android { - namespace = "com.walletconnect.wcmodal" - compileSdk = COMPILE_SDK - - defaultConfig { - minSdk = MIN_SDK - - aarMetadata { - minCompileSdk = MIN_SDK - } - - buildConfigField(type = "String", name = "SDK_VERSION", value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"") - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - - File("${rootDir.path}/gradle/consumer-rules").listFiles()?.let { proguardFiles -> - consumerProguardFiles(*proguardFiles) - } - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro") - } - } - - lint { - abortOnError = true - ignoreWarnings = true - warningsAsErrors = false - } - - compileOptions { - sourceCompatibility = jvmVersion - targetCompatibility = jvmVersion - } - kotlinOptions { - jvmTarget = jvmVersion.toString() - freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.time.ExperimentalTime" - } - buildFeatures { - compose = true - buildConfig = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() - } - - tasks.withType(Test::class.java) { - jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods") - } -} - -dependencies { - implementation(libs.bundles.androidxAppCompat) - implementation(libs.bundles.accompanist) - - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.compose.ui) - implementation(libs.androidx.compose.ui.tooling.preview) - implementation(libs.androidx.compose.material) - implementation(libs.androidx.compose.navigation) - implementation(libs.androidx.compose.lifecycle) - debugImplementation(libs.androidx.compose.ui.tooling) - debugImplementation(libs.androidx.compose.ui.test.manifest) - androidTestImplementation(libs.androidx.compose.ui.test.junit) - androidTestImplementation(libs.androidx.compose.navigation.testing) - - implementation(libs.coil) - implementation(libs.bundles.androidxLifecycle) - api(libs.bundles.androidxNavigation) - implementation(libs.qrCodeGenerator) - - testImplementation(libs.coroutines.test) - testImplementation(libs.turbine) - - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") - releaseImplementation("com.walletconnect:sign:$SIGN_VERSION") - releaseImplementation("com.walletconnect:modal-core:$MODAL_CORE_VERSION") - - debugImplementation(project(":core:android")) - debugImplementation(project(":protocol:sign")) - debugImplementation(project(":core:modal")) -} \ No newline at end of file diff --git a/product/walletconnectmodal/proguard-rules.pro b/product/walletconnectmodal/proguard-rules.pro deleted file mode 100644 index 481bb4348..000000000 --- a/product/walletconnectmodal/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/AndroidManifest.xml b/product/walletconnectmodal/src/main/AndroidManifest.xml deleted file mode 100644 index a5918e68a..000000000 --- a/product/walletconnectmodal/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/ClientMapper.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/ClientMapper.kt deleted file mode 100644 index ee797e5fe..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/ClientMapper.kt +++ /dev/null @@ -1,159 +0,0 @@ -package com.walletconnect.wcmodal.client - -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.sign.client.Sign - -// toModal() - -internal fun Sign.Model.ApprovedSession.toModal() = Modal.Model.ApprovedSession(topic, metaData, namespaces.toModal(), accounts) - -internal fun Map.toModal() = - mapValues { (_, namespace) -> Modal.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) } - -internal fun Sign.Model.RejectedSession.toModal() = Modal.Model.RejectedSession(topic, reason) - -internal fun Sign.Model.UpdatedSession.toModal() = Modal.Model.UpdatedSession(topic, namespaces.toModal()) - -internal fun Sign.Model.SessionEvent.toModal() = Modal.Model.SessionEvent(name, data) - -internal fun Sign.Model.Event.toModal() = Modal.Model.Event(topic, name, data, chainId) - -internal fun Sign.Model.Session.toModal() = Modal.Model.Session(pairingTopic, topic, expiry, namespaces.toModal(), metaData) - -internal fun Sign.Model.SessionProposal.toModal() = Modal.Model.SessionProposal( - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toMapOfClientNamespacesProposal(), - optionalNamespaces.toMapOfClientNamespacesProposal(), - properties, - proposerPublicKey, - relayProtocol, - relayData -) - -@JvmSynthetic -internal fun Map.toMapOfClientNamespacesProposal(): Map = - mapValues { (_, namespace) -> - Modal.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -internal fun Sign.Model.DeletedSession.toModal() = when (this) { - is Sign.Model.DeletedSession.Error -> Modal.Model.DeletedSession.Error(error) - is Sign.Model.DeletedSession.Success -> Modal.Model.DeletedSession.Success(topic, reason) -} - -internal fun Sign.Model.SessionRequestResponse.toModal() = Modal.Model.SessionRequestResponse(topic, chainId, method, result.toModal()) - -internal fun Sign.Model.JsonRpcResponse.toModal() = when (this) { - is Sign.Model.JsonRpcResponse.JsonRpcError -> Modal.Model.JsonRpcResponse.JsonRpcError(id, code, message) - is Sign.Model.JsonRpcResponse.JsonRpcResult -> Modal.Model.JsonRpcResponse.JsonRpcResult(id, result) -} - -@JvmSynthetic -internal fun Sign.Model.ExpiredProposal.toModal(): Modal.Model.ExpiredProposal = Modal.Model.ExpiredProposal(pairingTopic, proposerPublicKey) - -@JvmSynthetic -internal fun Sign.Model.ExpiredRequest.toModal(): Modal.Model.ExpiredRequest = Modal.Model.ExpiredRequest(topic, id) - - -@JvmSynthetic -internal fun Sign.Model.SessionAuthenticateResponse.toModal(): Modal.Model.SessionAuthenticateResponse = - when (this) { - is Sign.Model.SessionAuthenticateResponse.Result -> Modal.Model.SessionAuthenticateResponse.Result(id, cacaos.toClient(), session?.toModal()) - is Sign.Model.SessionAuthenticateResponse.Error -> Modal.Model.SessionAuthenticateResponse.Error(id, code, message) - } - -@JvmSynthetic -internal fun List.toClient(): List = this.map { - with(it) { - Modal.Model.Cacao( - Modal.Model.Cacao.Header(header.t), - Modal.Model.Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Modal.Model.Cacao.Signature(signature.t, signature.s, signature.m) - ) - } -} - -internal fun Sign.Model.ConnectionState.toModal() = Modal.Model.ConnectionState(isAvailable) - -internal fun Sign.Model.Error.toModal() = Modal.Model.Error(throwable) - -internal fun Sign.Params.Disconnect.toModal() = Modal.Params.Disconnect(sessionTopic) - -internal fun Sign.Model.SentRequest.toModal() = Modal.Model.SentRequest(requestId, sessionTopic, method, params, chainId) - -internal fun Sign.Model.Ping.Success.toModal() = Modal.Model.Ping.Success(topic) - -internal fun Sign.Model.Ping.Error.toModal() = Modal.Model.Ping.Error(error) - -// toSign() -internal fun Modal.Params.Connect.toSign() = Sign.Params.Connect(namespaces?.toSign(), optionalNamespaces?.toSign(), properties, pairing) - -internal fun Modal.Params.Authenticate.toSign(): Sign.Params.Authenticate = with(this) { - Sign.Params.Authenticate( - pairingTopic, - chains = chains, - domain = domain, - uri = uri, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - methods = methods, - expiry = expiry - ) -} - -internal fun Modal.Model.PayloadParams.toSign(): Sign.Model.PayloadParams = with(this) { - Sign.Model.PayloadParams( - chains = chains, - type = type ?: CacaoType.CAIP222.header, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat - ) -} - -internal fun Map.toSign() = mapValues { (_, namespace) -> Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) } - -internal fun Modal.Params.Disconnect.toSign() = Sign.Params.Disconnect(sessionTopic) - -internal fun Modal.Params.Ping.toSign() = Sign.Params.Ping(topic) - -internal fun Modal.Params.Request.toSign() = Sign.Params.Request(sessionTopic, method, params, chainId, expiry) - - -internal fun Modal.Listeners.SessionPing.toSign() = object : Sign.Listeners.SessionPing { - override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { - this@toSign.onSuccess(pingSuccess.toModal()) - } - - override fun onError(pingError: Sign.Model.Ping.Error) { - this@toSign.onError(pingError.toModal()) - } - -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/Modal.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/Modal.kt deleted file mode 100644 index b3ae83916..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/Modal.kt +++ /dev/null @@ -1,231 +0,0 @@ -package com.walletconnect.wcmodal.client - -import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import java.net.URI - -object Modal { - - sealed interface Listeners { - interface SessionPing : Listeners { - fun onSuccess(pingSuccess: Model.Ping.Success) - fun onError(pingError: Model.Ping.Error) - } - } - - sealed class Params { - data class Init( - val core: CoreClient, - val excludedWalletIds: List = listOf(), - val recommendedWalletsIds: List = listOf(), - val sessionParams: SessionParams? = null, - ) : Params() - - data class Connect( - val namespaces: Map? = null, - val optionalNamespaces: Map? = null, - val properties: Map? = null, - val pairing: Core.Model.Pairing, - ) : Params() - - data class Authenticate( - val pairingTopic: String? = null, - val chains: List, - val domain: String, - val nonce: String, - val uri: String, - val nbf: String? = null, - val exp: String? = null, - val statement: String? = null, - val requestId: String? = null, - val resources: List? = null, - val methods: List? = null, - val expiry: Long? = null - ) : Params() - - data class Disconnect(val sessionTopic: String) : Params() - - data class Ping(val topic: String) : Params() - - data class Request( - val sessionTopic: String, - val method: String, - val params: String, - val chainId: String, - val expiry: Long? = null, - ) : Params() - - data class SessionParams( - val requiredNamespaces: Map, - val optionalNamespaces: Map? = null, - val properties: Map? = null, - ) - } - - sealed class Model { - data class Error(val throwable: Throwable) : Model() - - sealed class Namespace : Model() { - data class Proposal( - val chains: List? = null, - val methods: List, - val events: List, - ) : Namespace() - - data class Session( - val chains: List? = null, - val accounts: List, - val methods: List, - val events: List, - ) : Namespace() - } - - data class PayloadParams( - val chains: List, - val domain: String, - val nonce: String, - val aud: String, - val type: String?, - val nbf: String?, - val iat: String, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List? - ) : Model() - - data class ApprovedSession( - val topic: String, - val metaData: Core.Model.AppMetaData?, - val namespaces: Map, - val accounts: List, - ) : Model() - - data class RejectedSession(val topic: String, val reason: String) : Model() - - data class UpdatedSession( - val topic: String, - val namespaces: Map, - ) : Model() - - data class SessionEvent( - val name: String, - val data: String, - ) : Model() - - data class Event( - val topic: String, - val name: String, - val data: String, - val chainId: String, - ) : Model() - - sealed class DeletedSession : Model() { - data class Success(val topic: String, val reason: String) : DeletedSession() - data class Error(val error: Throwable) : DeletedSession() - } - - data class Session( - val pairingTopic: String, - val topic: String, - val expiry: Long, - val namespaces: Map, - val metaData: Core.Model.AppMetaData?, - ) : Model() { - val redirect: String? = metaData?.redirect - } - - data class SessionRequestResponse( - val topic: String, - val chainId: String?, - val method: String, - val result: JsonRpcResponse, - ) : Model() - - data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : Model() - - data class SessionProposal( - val pairingTopic: String, - val name: String, - val description: String, - val url: String, - val icons: List, - val redirect: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map, - val properties: Map?, - val proposerPublicKey: String, - val relayProtocol: String, - val relayData: String?, - ) : Model() - - data class ExpiredRequest(val topic: String, val id: Long) : Model() - - data class ConnectionState( - val isAvailable: Boolean, - ) : Model() - - - sealed class JsonRpcResponse : Model() { - abstract val id: Long - val jsonrpc: String = "2.0" - - data class JsonRpcResult( - override val id: Long, - val result: String, - ) : JsonRpcResponse() - - data class JsonRpcError( - override val id: Long, - val code: Int, - val message: String, - ) : JsonRpcResponse() - } - - sealed class SessionAuthenticateResponse : Model() { - data class Result(val id: Long, val cacaos: List, val session: Session?) : SessionAuthenticateResponse() - data class Error(val id: Long, val code: Int, val message: String) : SessionAuthenticateResponse() - } - - data class Cacao( - val header: Header, - val payload: Payload, - val signature: Signature, - ) : Model() { - @Keep - data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface - data class Header(val t: String) : Model() - data class Payload( - val iss: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() { - val address: String get() = Issuer(iss).address - } - } - - data class SentRequest( - val requestId: Long, - val sessionTopic: String, - val method: String, - val params: String, - val chainId: String, - ) : Model() - - sealed class Ping : Model() { - data class Success(val topic: String) : Ping() - data class Error(val error: Throwable) : Ping() - } - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/WalletConnectModal.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/WalletConnectModal.kt deleted file mode 100644 index be6d7e62c..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/client/WalletConnectModal.kt +++ /dev/null @@ -1,236 +0,0 @@ -package com.walletconnect.wcmodal.client - -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.common.exceptions.SignClientAlreadyInitializedException -import com.walletconnect.wcmodal.di.walletConnectModalModule -import com.walletconnect.wcmodal.domain.WalletConnectModalDelegate -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - -object WalletConnectModal { - - internal var excludedWalletsIds: List = listOf() - internal var recommendedWalletsIds: List = listOf() - - private var _sessionParams: Modal.Params.SessionParams? = null - val sessionParams: Modal.Params.SessionParams - get() = requireNotNull(_sessionParams) { "Be sure to set the SessionParams in either the Modal.Params.Init or WalletConnectModal.setSessionParams." } - - interface ModalDelegate { - fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) - fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) - fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) - - @Deprecated( - message = "Use onSessionEvent(Modal.Model.Event) instead. Using both will result in duplicate events.", - replaceWith = ReplaceWith(expression = "onSessionEvent(sessionEvent)") - ) - fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) - fun onSessionEvent(sessionEvent: Modal.Model.Event) {} - fun onSessionExtend(session: Modal.Model.Session) - fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) - - //Responses - fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) - fun onSessionAuthenticateResponse(sessionUpdateResponse: Modal.Model.SessionAuthenticateResponse) {} - - // Utils - fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) - fun onRequestExpired(request: Modal.Model.ExpiredRequest) - fun onConnectionStateChange(state: Modal.Model.ConnectionState) - fun onError(error: Modal.Model.Error) - } - - fun initialize( - init: Modal.Params.Init, - onSuccess: () -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - SignClient.initialize( - init = Sign.Params.Init(init.core), - onSuccess = { onInitializedClient(init, onSuccess, onError) }, - onError = { error -> - if (error.throwable is SignClientAlreadyInitializedException) { - onInitializedClient(init, onSuccess, onError) - } else { - onError(Modal.Model.Error(error.throwable)) - return@initialize - } - } - ) - } - - private fun onInitializedClient( - init: Modal.Params.Init, - onSuccess: () -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - this.excludedWalletsIds = init.excludedWalletIds - this.recommendedWalletsIds = init.recommendedWalletsIds - _sessionParams = init.sessionParams - runCatching { - wcKoinApp.modules(walletConnectModalModule()) - setInternalDelegate(WalletConnectModalDelegate) - }.onFailure { error -> return@onInitializedClient onError(Modal.Model.Error(error)) } - onSuccess() - } - - @Throws(IllegalStateException::class) - fun setDelegate(delegate: ModalDelegate) { - - WalletConnectModalDelegate.connectionState.onEach { event -> delegate.onConnectionStateChange(event) }.launchIn(scope) - WalletConnectModalDelegate.wcEventModels.onEach { event -> - when (event) { - is Modal.Model.ApprovedSession -> delegate.onSessionApproved(event) - is Modal.Model.DeletedSession.Success -> delegate.onSessionDelete(event) - is Modal.Model.Error -> delegate.onError(event) - is Modal.Model.RejectedSession -> delegate.onSessionRejected(event) - is Modal.Model.Session -> delegate.onSessionExtend(event) - //todo: how to notify developer to not us both at the same time - is Modal.Model.SessionEvent -> delegate.onSessionEvent(event) - is Modal.Model.Event -> delegate.onSessionEvent(event) - is Modal.Model.SessionRequestResponse -> delegate.onSessionRequestResponse(event) - is Modal.Model.UpdatedSession -> delegate.onSessionUpdate(event) - is Modal.Model.ExpiredProposal -> delegate.onProposalExpired(event) - is Modal.Model.ExpiredRequest -> delegate.onRequestExpired(event) - is Modal.Model.SessionAuthenticateResponse -> delegate.onSessionAuthenticateResponse(event) - else -> Unit - } - }.launchIn(scope) - } - - @Throws(IllegalStateException::class) - private fun setInternalDelegate(delegate: ModalDelegate) { - val signDelegate = object : SignClient.DappDelegate { - override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) { - delegate.onSessionApproved(approvedSession.toModal()) - } - - override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) { - delegate.onSessionRejected(rejectedSession.toModal()) - } - - override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) { - delegate.onSessionUpdate(updatedSession.toModal()) - } - - override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) { - delegate.onSessionEvent(sessionEvent.toModal()) - } - - override fun onSessionEvent(sessionEvent: Sign.Model.Event) { - delegate.onSessionEvent(sessionEvent.toModal()) - } - - override fun onSessionExtend(session: Sign.Model.Session) { - delegate.onSessionExtend(session.toModal()) - } - - override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { - delegate.onSessionDelete(deletedSession.toModal()) - } - - override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) { - delegate.onSessionRequestResponse(response.toModal()) - } - - override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { - delegate.onProposalExpired(proposal.toModal()) - } - - override fun onRequestExpired(request: Sign.Model.ExpiredRequest) { - delegate.onRequestExpired(request.toModal()) - } - - override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) { - delegate.onSessionAuthenticateResponse(sessionAuthenticateResponse.toModal()) - } - - override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { - delegate.onConnectionStateChange(state.toModal()) - } - - override fun onError(error: Sign.Model.Error) { - delegate.onError(error.toModal()) - } - } - SignClient.setDappDelegate(signDelegate) - } - - fun setSessionParams(sessionParams: Modal.Params.SessionParams) { - _sessionParams = sessionParams - } - - @Deprecated( - message = "Replaced with the same name method but onSuccess callback returns a Pairing URL", - replaceWith = ReplaceWith(expression = "fun connect(connect: Modal.Params.Connect, onSuccess: (String) -> Unit, onError: (Modal.Model.Error) -> Unit)") - ) - fun connect( - connect: Modal.Params.Connect, - onSuccess: () -> Unit, - onError: (Modal.Model.Error) -> Unit, - ) { - SignClient.connect( - connect = connect.toSign(), - onSuccess = onSuccess, - onError = { onError(it.toModal()) } - ) - } - - fun connect( - connect: Modal.Params.Connect, - onSuccess: (String) -> Unit, - onError: (Modal.Model.Error) -> Unit, - ) { - SignClient.connect( - connect = connect.toSign(), - onSuccess = { url -> onSuccess(url) }, - onError = { onError(it.toModal()) } - ) - } - - fun request(request: Modal.Params.Request, onSuccess: (Modal.Model.SentRequest) -> Unit = {}, onError: (Modal.Model.Error) -> Unit) { - SignClient.request( - request.toSign(), - { onSuccess(it.toModal()) }, - { onError(it.toModal()) } - ) - } - - fun ping(ping: Modal.Params.Ping, sessionPing: Modal.Listeners.SessionPing? = null) { - SignClient.ping( - ping = ping.toSign(), - sessionPing = sessionPing?.toSign() - ) - } - - fun disconnect(disconnect: Modal.Params.Disconnect, onSuccess: (Modal.Params.Disconnect) -> Unit = {}, onError: (Modal.Model.Error) -> Unit) { - SignClient.disconnect( - disconnect = disconnect.toSign(), - onSuccess = { onSuccess(it.toModal()) }, - onError = { onError(it.toModal()) } - ) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getListOfActiveSessions() = SignClient.getListOfActiveSessions().map { it.toModal() } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getListOfProposals() = SignClient.getSessionProposals().map { it.toModal() } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getActiveSessionByTopic(topic: String) = SignClient.getActiveSessionByTopic(topic)?.toModal() - -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/di/WalletConnectModalModule.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/di/WalletConnectModalModule.kt deleted file mode 100644 index 66ef39c79..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/di/WalletConnectModalModule.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.wcmodal.di - -import com.walletconnect.wcmodal.domain.RecentWalletsRepository -import com.walletconnect.wcmodal.domain.usecase.GetRecentWalletUseCase -import com.walletconnect.wcmodal.domain.usecase.SaveRecentWalletUseCase -import org.koin.dsl.module - -internal fun walletConnectModalModule() = module { - - single { RecentWalletsRepository(get()) } - - factory { GetRecentWalletUseCase(get()) } - factory { SaveRecentWalletUseCase(get()) } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/RecentWalletsRepository.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/RecentWalletsRepository.kt deleted file mode 100644 index 667c4c238..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/RecentWalletsRepository.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.wcmodal.domain - -import android.content.SharedPreferences - -private const val RECENT_WALLET_ID = "recent_wallet_id" - -class RecentWalletsRepository( - private val sharedPreferences: SharedPreferences -) { - - fun saveRecentWalletId(id: String) = sharedPreferences.edit().putString(RECENT_WALLET_ID, id).apply() - - fun getRecentWalletId() = sharedPreferences.getString(RECENT_WALLET_ID, null) -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/WalletConnectModalDelegate.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/WalletConnectModalDelegate.kt deleted file mode 100644 index 9c1e524e7..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/WalletConnectModalDelegate.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.walletconnect.wcmodal.domain - -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal object WalletConnectModalDelegate : WalletConnectModal.ModalDelegate { - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() - val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() - - private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) - val connectionState: SharedFlow = _connectionState.asSharedFlow() - - override fun onConnectionStateChange(state: Modal.Model.ConnectionState) { - scope.launch { - _connectionState.emit(state) - } - } - - - override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { - scope.launch { - _wcEventModels.emit(approvedSession) - } - } - - override fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) { - scope.launch { - _wcEventModels.emit(rejectedSession) - } - } - - override fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) { - scope.launch { - _wcEventModels.emit(updatedSession) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) { - scope.launch { - _wcEventModels.emit(sessionEvent) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.Event) { - scope.launch { - _wcEventModels.emit(sessionEvent) - } - } - - override fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) { - scope.launch { - _wcEventModels.emit(deletedSession) - } - } - - override fun onSessionExtend(session: Modal.Model.Session) { - scope.launch { - _wcEventModels.emit(session) - } - } - - override fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) { - scope.launch { - _wcEventModels.emit(response) - } - } - - override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) { - scope.launch { - _wcEventModels.emit(proposal) - } - } - - override fun onRequestExpired(request: Modal.Model.ExpiredRequest) { - scope.launch { - _wcEventModels.emit(request) - } - } - - override fun onSessionAuthenticateResponse(sessionUpdateResponse: Modal.Model.SessionAuthenticateResponse) { - scope.launch { - _wcEventModels.emit(sessionUpdateResponse) - } - } - - override fun onError(error: Modal.Model.Error) { - scope.launch { - _wcEventModels.emit(error) - } - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/dataStore/WalletDataStore.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/dataStore/WalletDataStore.kt deleted file mode 100644 index 9a0f8533c..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/dataStore/WalletDataStore.kt +++ /dev/null @@ -1,175 +0,0 @@ -package com.walletconnect.wcmodal.domain.dataStore - -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.util.Empty -import com.walletconnect.wcmodal.client.WalletConnectModal -import com.walletconnect.wcmodal.domain.usecase.GetRecentWalletUseCase -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine - -private const val WCM_SDK = "wcm" - -private data class ListingData( - var page: Int = 1, - var totalCount: Int = 0, - var wallets: List = listOf() -) { - fun addNextPage(wallets: List) { - page += 1 - this.wallets = this.wallets + wallets - } -} - -internal class WalletDataSource( - private val showError: (Exception) -> Unit -) { - private val getWalletsUseCase: GetWalletsUseCaseInterface = wcKoinApp.koin.get() - private val getWalletsAppDataUseCase: GetInstalledWalletsIdsUseCaseInterface = wcKoinApp.koin.get() - private val getRecentWalletUseCase: GetRecentWalletUseCase = wcKoinApp.koin.get() - private val getSampleWalletsUseCase: GetSampleWalletsUseCaseInterface = wcKoinApp.koin.get() - - - private var installedWalletsIds: List = listOf() - - private var walletsListingData = ListingData() - private var searchListingData = ListingData() - - var searchPhrase = String.Empty - - val wallets: List - get() = walletsListingData.wallets - - private fun getPriorityWallets() = (getRecentWalletUseCase()?.let { listOf(it) } ?: listOf()) + installedWalletsIds + WalletConnectModal.recommendedWalletsIds - - private val searchState: MutableStateFlow = MutableStateFlow(WalletsData.empty()) - val walletState: MutableStateFlow = MutableStateFlow(WalletsData.empty()) - - val searchWalletsState = combine(walletState, searchState) { state, search -> - if (searchPhrase.isEmpty()) { state } else { search } - } - - suspend fun fetchInitialWallets() { - walletState.value = WalletsData.refresh() - try { - fetchWalletsAppData() - val installedWallets = fetchInstalledAndRecommendedWallets() - val samples = getSampleWalletsUseCase() - val walletsListing = getWalletsUseCase(sdkType = WCM_SDK, page = 1, excludeIds = getPriorityWallets() + WalletConnectModal.excludedWalletsIds) - walletsListingData = ListingData( - page = 1, - totalCount = walletsListing.totalCount + samples.size, - wallets = (samples + installedWallets.wallets + walletsListing.wallets).mapRecentWallet(getRecentWalletUseCase()) - ) - walletState.value = WalletsData.submit(walletsListingData.wallets) - } catch (exception: Exception) { - showError(exception) - walletState.value = WalletsData.error(error = exception) - } - } - - fun updateRecentWallet(walletId: String) { - walletState.tryEmit(WalletsData.append(walletState.value.wallets.mapRecentWallet(walletId))) - } - - private suspend fun fetchWalletsAppData() { - installedWalletsIds = getWalletsAppDataUseCase(sdkType = WCM_SDK) - } - - private suspend fun fetchInstalledAndRecommendedWallets() = getWalletsUseCase( - sdkType = WCM_SDK, - page = 1, - includes = getPriorityWallets(), - excludeIds = WalletConnectModal.excludedWalletsIds - ) - - suspend fun fetchMoreWallets() { - if (searchPhrase.isEmpty()) { - if (walletsListingData.wallets.size < walletsListingData.totalCount) { - try { - walletState.value = WalletsData.append(walletsListingData.wallets) - val response = getWalletsUseCase(sdkType = WCM_SDK, page = walletsListingData.page + 1, excludeIds = getPriorityWallets() + WalletConnectModal.excludedWalletsIds) - walletsListingData.addNextPage(response.wallets) - walletState.value = WalletsData.submit(walletsListingData.wallets) - } catch (exception: Exception) { - showError(exception) - walletState.value = WalletsData.error(walletsListingData.wallets, error = exception) - } - } - } else { - fetchNextSearchPage() - } - } - - fun clearSearch() { - searchState.value = WalletsData.empty() - searchPhrase = String.Empty - } - - suspend fun searchWallet(searchPhrase: String) { - if (searchPhrase.isEmpty()) { - clearSearch() - } else { - this.searchPhrase = searchPhrase - if (walletsListingData.wallets.size < walletsListingData.totalCount) { - try { - searchState.value = WalletsData.refresh() - val searchResponse = getWalletsUseCase(sdkType = WCM_SDK, search = searchPhrase, page = 1, excludeIds = WalletConnectModal.excludedWalletsIds) - searchListingData = ListingData(page = 1, totalCount = searchResponse.totalCount, wallets = searchResponse.wallets) - searchState.value = WalletsData.submit(wallets = searchListingData.wallets) - } catch (exception: Exception) { - showError(exception) - searchState.value = WalletsData.error(walletsListingData.wallets, error = exception) - } - } else { - val searchedWallets = walletsListingData.wallets.filteredWallets(searchPhrase) - searchState.value = WalletsData.submit(wallets = searchedWallets) - } - } - } - - private suspend fun fetchNextSearchPage() { - if (searchListingData.wallets.size < searchListingData.totalCount) { - try { - searchState.value = WalletsData.append(searchListingData.wallets) - val searchResponse = getWalletsUseCase(sdkType = WCM_SDK, search = searchPhrase, page = searchListingData.page + 1, excludeIds = WalletConnectModal.excludedWalletsIds) - searchListingData.addNextPage(searchResponse.wallets) - searchState.value = WalletsData.submit(searchListingData.wallets) - } catch (exception: Exception) { - showError(exception) - searchState.value = WalletsData.error(searchListingData.wallets, error = exception) - } - } - } - - fun getWallet(walletId: String?) = walletsListingData.wallets.find { wallet -> wallet.id == walletId } -} - -private fun List.filteredWallets(value: String): List = this.filter { it.name.startsWith(prefix = value, ignoreCase = true) } - -private fun List.mapRecentWallet(id: String?) = map { - it.apply { it.isRecent = it.id == id } -}.sortedWith(compareByDescending { it.isRecent }.thenByDescending { it.isWalletInstalled }) - - -internal data class WalletsData( - val wallets: List = listOf(), - val loadingState: LoadingState? = null, - val error: Exception? = null -) { - companion object { - fun empty() = WalletsData() - - fun refresh() = WalletsData(loadingState = LoadingState.REFRESH) - - fun submit(wallets: List) = WalletsData(wallets) - - fun append(wallets: List) = WalletsData(wallets, LoadingState.APPEND) - - fun error(wallets: List = listOf(), error: Exception) = WalletsData(wallets = wallets, error = error) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/GetRecentWalletUseCase.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/GetRecentWalletUseCase.kt deleted file mode 100644 index eea08e0d2..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/GetRecentWalletUseCase.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.wcmodal.domain.usecase - -import com.walletconnect.wcmodal.domain.RecentWalletsRepository - -class GetRecentWalletUseCase(private val repository: RecentWalletsRepository) { - operator fun invoke() = repository.getRecentWalletId() -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/SaveRecentWalletUseCase.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/SaveRecentWalletUseCase.kt deleted file mode 100644 index 8d7250aca..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/domain/usecase/SaveRecentWalletUseCase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.wcmodal.domain.usecase - -import com.walletconnect.wcmodal.domain.RecentWalletsRepository - -class SaveRecentWalletUseCase( - private val repository: RecentWalletsRepository -) { - operator fun invoke(id: String) = repository.saveRecentWalletId(id) -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModal.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModal.kt deleted file mode 100644 index 90eebabbc..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModal.kt +++ /dev/null @@ -1,109 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.wcmodal.ui - -import android.content.Context -import android.widget.Toast -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController -import com.google.accompanist.navigation.animation.rememberAnimatedNavController -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.domain.WalletConnectModalDelegate -import com.walletconnect.wcmodal.ui.components.ModalRoot -import com.walletconnect.wcmodal.ui.components.RoundedMainButton -import com.walletconnect.wcmodal.ui.navigation.ModalNavGraph -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.onEach - -@Composable -internal fun WalletConnectModal( - navController: NavController -) { - WalletConnectModalComponent( - closeModal = { navController.popBackStack() } - ) -} - -@ExperimentalAnimationApi -@Composable -internal fun WalletConnectModalComponent(closeModal: () -> Unit) { - val context = LocalContext.current - val viewModel: WalletConnectModalViewModel = viewModel() - val navController = rememberAnimatedNavController() - LaunchedEffect(Unit) { - WalletConnectModalDelegate - .wcEventModels - .onEach { event -> event?.handleEvent(context, closeModal) } - .collect() - } - - ModalRoot( - navController = navController, - closeModal = closeModal, - ) { - ModalNavGraph( - navController = navController, - viewModel = viewModel - ) - } -} - - -@Composable -internal fun LoadingModalState() { - Box( - modifier = Modifier - .fillMaxWidth() - .height(300.dp) - ) { - CircularProgressIndicator(color = ModalTheme.colors.main, modifier = Modifier.align(Alignment.Center)) - } -} - -@Composable -internal fun ErrorModalState(retry: () -> Unit) { - Column( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = "Something went wrong", style = TextStyle(color = ModalTheme.colors.onBackgroundColor, fontSize = 18.sp)) - VerticalSpacer(height = 10.dp) - RoundedMainButton( - text = "Retry", - onClick = { retry() }, - ) - } -} - -private fun Modal.Model.handleEvent(context: Context, closeModal: () -> Unit) { - when (this) { - is Modal.Model.ApprovedSession -> { - closeModal() - } - is Modal.Model.Error -> { - Toast.makeText(context, "Something went wrong", Toast.LENGTH_SHORT).show() - } - else -> Unit - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalEvents.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalEvents.kt deleted file mode 100644 index 81c0b10ae..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalEvents.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.wcmodal.ui - -sealed class WalletConnectModalEvents { - object SessionApproved: WalletConnectModalEvents() - object SessionRejected: WalletConnectModalEvents() - object NoAction: WalletConnectModalEvents() - object InvalidState: WalletConnectModalEvents() -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalNav.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalNav.kt deleted file mode 100644 index 72e1156e0..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalNav.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:OptIn(ExperimentalMaterialNavigationApi::class) - -package com.walletconnect.wcmodal.ui - -import androidx.annotation.IdRes -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.fragment.dialog -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.google.accompanist.navigation.material.bottomSheet -import com.walletconnect.wcmodal.ui.navigation.Route - -fun NavGraphBuilder.walletConnectModal() { - dialog(route = Route.WalletConnectModalRoot.path) -} - -fun NavController.openWalletConnectModal(@IdRes id: Int) = navigate(id) - -fun NavController.openWalletConnectModal() = navigate(Route.WalletConnectModalRoot.path) - -fun NavGraphBuilder.walletConnectModalGraph(navController: NavController) { - bottomSheet(route = Route.WalletConnectModalRoot.path) { - WalletConnectModal(navController = navController) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalSheet.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalSheet.kt deleted file mode 100644 index 5fbdda975..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalSheet.kt +++ /dev/null @@ -1,69 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.wcmodal.ui - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.activity.ComponentDialog -import androidx.activity.OnBackPressedCallback -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.walletconnect.modal.utils.theme.themeColor -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.theme.WalletConnectModalTheme - -class WalletConnectModalSheet : BottomSheetDialogFragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - requireContext().setTheme(R.style.WalletConnectModalTheme) - super.onCreate(savedInstanceState) - } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - - val accentColor = requireContext().themeColor(R.attr.accentColor) - val onAccentColor = requireContext().themeColor(R.attr.onAccentColor) - - return ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - WalletConnectModalTheme( - accentColor = accentColor, - onAccentColor = onAccentColor, - ) { - ModalComposeView() - } - } - } - } - - @Composable - private fun ModalComposeView() { - val navController = rememberNavController() - (dialog as? ComponentDialog)?.onBackPressedDispatcher?.addCallback( - this@WalletConnectModalSheet, - onBackPressedCallback(navController) - ) - WalletConnectModalComponent(closeModal = { this@WalletConnectModalSheet.dismiss() }) - } - - - private fun onBackPressedCallback(navController: NavHostController) = - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - if (navController.popBackStack().not()) { - dismiss() - } - } - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModel.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModel.kt deleted file mode 100644 index bc072744f..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModel.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.walletconnect.wcmodal.ui - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.walletconnect.android.CoreClient -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.foundation.util.Logger -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import com.walletconnect.wcmodal.domain.dataStore.WalletDataSource -import com.walletconnect.wcmodal.domain.dataStore.WalletsData -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch - -internal class WalletConnectModalViewModel : ViewModel() { - - private val logger: Logger = wcKoinApp.koin.get() - private val walletsDataStore = WalletDataSource { handleError(it) } - - val walletsState: StateFlow = walletsDataStore.searchWalletsState.stateIn(viewModelScope, SharingStarted.Lazily, WalletsData.empty()) - - val uiState: StateFlow>> = walletsDataStore.walletState.map { pagingData -> - when { - pagingData.error != null -> UiState.Error(pagingData.error) - pagingData.loadingState == LoadingState.REFRESH -> UiState.Loading() - else -> UiState.Success(pagingData.wallets) - } - }.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = UiState.Loading()) - - val searchPhrase - get() = walletsDataStore.searchPhrase - - - init { - fetchInitialWallets() - } - - fun fetchInitialWallets() { - viewModelScope.launch { walletsDataStore.fetchInitialWallets() } - } - - fun connect(onSuccess: (String) -> Unit) { - try { - val pairing = - CoreClient.Pairing.create { error -> - throw IllegalStateException("Creating Pairing failed: ${error.throwable.stackTraceToString()}") - }!! - - val sessionParams = WalletConnectModal.sessionParams - val connectParams = Modal.Params.Connect( - sessionParams.requiredNamespaces, - sessionParams.optionalNamespaces, - sessionParams.properties, - pairing - ) - - WalletConnectModal.connect( - connect = connectParams, - onSuccess = { url -> viewModelScope.launch { onSuccess(url) } }, - onError = { handleError(it.throwable) } - ) - } catch (e: Exception) { - handleError(e) - } - } - - fun fetchMoreWallets() { - viewModelScope.launch { walletsDataStore.fetchMoreWallets() } - } - - fun search(searchPhrase: String) { - viewModelScope.launch { walletsDataStore.searchWallet(searchPhrase) } - } - - fun saveRecentWallet(wallet: Wallet) { - walletsDataStore.updateRecentWallet(wallet.id) - } - - fun clearSearch() = walletsDataStore.clearSearch() - - fun getWallet(walletId: String?) = walletsDataStore.getWallet(walletId) - - fun getNotInstalledWallets() = walletsDataStore.wallets.filterNot { it.isWalletInstalled } - - private fun handleError(error: Throwable) { - logger.error(error) - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Buttons.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Buttons.kt deleted file mode 100644 index 0c92a9d5f..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Buttons.kt +++ /dev/null @@ -1,123 +0,0 @@ -package com.walletconnect.wcmodal.ui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.walletconnect.wcmodal.ui.theme.ModalTheme - -@Composable -internal fun MainButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { - val contentColor = ModalTheme.colors.onMainColor - val background = ModalTheme.colors.main - Button( - shape = RoundedCornerShape(12.dp), - modifier = modifier, - colors = ButtonDefaults.buttonColors( - backgroundColor = background, - contentColor = contentColor, - ), - onClick = { - onClick() - }, - ) { - Text(text = text, color = contentColor) - } -} - -@Composable -internal fun RoundedOutLineButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - startIcon: (@Composable () -> Unit)? = null, - endIcon: (@Composable () -> Unit)? = null, -) { - RoundedButton( - onClick = onClick, - modifier = modifier - .border( - width = 1.dp, - color = ModalTheme.colors.dividerColor, - RoundedCornerShape(18.dp) - ) - .padding(vertical = 8.dp, horizontal = 12.dp), - startIcon = startIcon, endIcon = endIcon - ) { - Text( - text = text, - style = TextStyle( - color = ModalTheme.colors.main, - textAlign = TextAlign.Center - ), - ) - } -} - -@Composable -internal fun RoundedMainButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - startIcon: (@Composable () -> Unit)? = null, - endIcon: (@Composable () -> Unit)? = null, -) { - RoundedButton( - onClick = onClick, - modifier = modifier - .background( - ModalTheme.colors.main, - shape = RoundedCornerShape(18.dp) - ) - .padding(vertical = 8.dp, horizontal = 12.dp), - startIcon = startIcon, endIcon = endIcon - ) { - Text( - text = text, - style = TextStyle( - color = ModalTheme.colors.onMainColor, - textAlign = TextAlign.Center - ), - ) - } -} - -@Composable -private fun RoundedButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, - startIcon: (@Composable () -> Unit)? = null, - endIcon: (@Composable () -> Unit)? = null, - content: @Composable RowScope.() -> Unit -) { - Row( - modifier = Modifier - .clickable { onClick() } - .then(modifier), - verticalAlignment = Alignment.CenterVertically, - ) { - startIcon?.let { - startIcon() - Spacer(modifier = Modifier.width(6.dp)) - } - content() - endIcon?.let { - Spacer(modifier = Modifier.width(6.dp)) - endIcon() - } - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/CircleButton.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/CircleButton.kt deleted file mode 100644 index a66ef9847..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/CircleButton.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.walletconnect.wcmodal.ui.components - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.modal.ui.components.common.roundedClickable -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.navigation.Route -import com.walletconnect.wcmodal.ui.preview.ComponentPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme - - -@Composable -private fun CircleButtonWithIcon( - icon: @Composable () -> Unit, - backgroundColor: Color, - onClick: () -> Unit -) { - Box( - modifier = Modifier - .size(28.dp) - .shadow(elevation = 4.dp, shape = CircleShape) - .background(backgroundColor) - .roundedClickable(onClick = onClick), - contentAlignment = Alignment.Center - ) { - icon() - } -} - -@Composable -internal fun QuestionMarkIconButton(navController: NavController) { - val currentPath = navController.currentBackStackEntryAsState().value?.destination?.route - val tint: Color - val background: Color - val onClick: () -> Unit - if (currentPath == Route.Help.path) { - tint = ModalTheme.colors.background - background = ModalTheme.colors.onBackgroundColor - onClick = { navController.popBackStack() } - } else { - tint = ModalTheme.colors.onBackgroundColor - background = ModalTheme.colors.background - onClick = { navController.navigate(Route.Help.path) } - } - CircleButtonWithIcon( - icon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_question_mark), - contentDescription = "QuestionMark", - colorFilter = ColorFilter.tint(tint) - ) - }, - backgroundColor = background, - onClick = onClick - ) -} - -@Composable -internal fun CloseIconButton(onClick: () -> Unit) { - CircleButtonWithIcon( - icon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), - contentDescription = "CloseIcon", - colorFilter = ColorFilter.tint(ModalTheme.colors.onBackgroundColor) - ) - }, - backgroundColor = ModalTheme.colors.background, - onClick = onClick - ) -} - -@Preview -@Composable -private fun CircleButtonsPreview() { - ComponentPreview { - CloseIconButton({}) - VerticalSpacer(4.dp) - QuestionMarkIconButton(navController = rememberNavController()) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Images.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Images.kt deleted file mode 100644 index 4096f4c73..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Images.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.wcmodal.ui.components - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import com.walletconnect.wcmodal.ui.theme.ModalTheme - -@Composable -internal fun ImageWithMainTint( - @DrawableRes icon: Int, - modifier: Modifier = Modifier, - contentDescription: String? = null, -) { - Image( - imageVector = ImageVector.vectorResource(id = icon), - contentDescription = contentDescription, - modifier = modifier, - colorFilter = ColorFilter.tint(ModalTheme.colors.main) - ) -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalRoot.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalRoot.kt deleted file mode 100644 index a30767877..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalRoot.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.walletconnect.wcmodal.ui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.modal.ui.components.logo.WalletConnectLogo -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.wcmodal.ui.theme.ProvideModalThemeComposition - -@Composable -internal fun ModalRoot( - navController: NavController, - closeModal: () -> Unit, - content: @Composable () -> Unit -) { - Column( - verticalArrangement = Arrangement.Bottom - ) { - ProvideModalThemeComposition { - Column( - modifier = Modifier.background(color = ModalTheme.colors.main) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp, horizontal = 20.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - WalletConnectLogo(modifier = Modifier.weight(1f), color = ModalTheme.colors.onMainColor) - CloseIconButton { closeModal() } - } - Column( - modifier = Modifier - .fillMaxWidth() - .background( - ModalTheme.colors.background, - shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp) - ) - ) { - content() - } - } - } - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalTopBar.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalTopBar.kt deleted file mode 100644 index c029304f8..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/ModalTopBar.kt +++ /dev/null @@ -1,170 +0,0 @@ -package com.walletconnect.wcmodal.ui.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.walletconnect.modal.ui.components.common.ClickableImage -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.modal.ui.model.search.SearchState -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.preview.ComponentPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme - -@Composable -internal fun ModalTopBar( - title: String, - endIcon: (@Composable () -> Unit)? = null, - onBackPressed: (() -> Unit)? = null -) { - Box( - modifier = Modifier - .height(60.dp) - .fillMaxWidth() - .padding(horizontal = 20.dp) - ) { - Text( - text = title, - style = TextStyle( - color = ModalTheme.colors.textColor, - fontSize = 20.sp - ), - modifier = Modifier.align(Alignment.Center) - ) - onBackPressed?.let { onBackClick -> - ClickableImage( - tint = ModalTheme.colors.main, - imageVector = ImageVector.vectorResource(id = R.drawable.chevron_left), - contentDescription = "BackArrow", - modifier = Modifier.align(Alignment.CenterStart), - onClick = onBackClick - ) - } - endIcon?.let { - Box(modifier = Modifier.align(Alignment.CenterEnd)) { - endIcon() - } - } - } -} - -@Composable -internal fun ModalSearchTopBar( - searchState: SearchState, - onBackPressed: (() -> Unit) -) { - val focusManager = LocalFocusManager.current - val state by searchState.state.collectAsState() - - Box( - modifier = Modifier - .height(60.dp) - .fillMaxWidth() - .padding(horizontal = 20.dp), - ) { - ClickableImage( - tint = ModalTheme.colors.main, - imageVector = ImageVector.vectorResource(id = R.drawable.chevron_left), - contentDescription = "BackArrow", - modifier = Modifier.align(Alignment.CenterStart), - onClick = onBackPressed - ) - BasicTextField( - value = state.searchValue, - onValueChange = searchState::onSearchValueChange, - textStyle = TextStyle(color = ModalTheme.colors.onBackgroundColor), - cursorBrush = SolidColor(ModalTheme.colors.main), - singleLine = true, - modifier = Modifier - .padding(horizontal = 30.dp) - .fillMaxWidth() - .align(Alignment.Center) - .background(ModalTheme.colors.background) - .border(width = 1.dp, color = ModalTheme.colors.main, shape = RoundedCornerShape(16.dp)) - .padding(2.dp), - keyboardActions = KeyboardActions(onSearch = { - searchState.onSearchSubmit() - focusManager.clearFocus(true) } - ), - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - decorationBox = { innerTextField -> - Row( - modifier = Modifier.padding(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - HorizontalSpacer(width = 4.dp) - Icon( - tint = ModalTheme.colors.secondaryTextColor, - imageVector = ImageVector.vectorResource(id = R.drawable.ic_search), - contentDescription = "lens", - ) - HorizontalSpacer(width = 8.dp) - Box { - if (searchState.searchValue.isBlank()) { - Text(text = "Search wallets", style = TextStyle(color = ModalTheme.colors.secondaryTextColor)) - } - innerTextField() - } - } - }, - ) - - } -} - -@Preview -@Composable -private fun PreviewWeb3TopBar() { - ComponentPreview { - ModalTopBar( - title = "Connect your wallet", - endIcon = { - ImageWithMainTint(icon = R.drawable.ic_scan) - }, - onBackPressed = null - ) - VerticalSpacer(height = 6.dp) - ModalTopBar( - title = "Scan the code", - endIcon = { - ImageWithMainTint(icon = R.drawable.ic_copy) - }, - onBackPressed = {}) - ModalTopBar( - title = "What is wallet?", - onBackPressed = {} - ) - ModalSearchTopBar( - searchState = SearchState(searchPhrase = "", onSearchSubmit = {}, onClearInput = {}), - onBackPressed = {} - ) - ModalSearchTopBar( - searchState = SearchState(searchPhrase = "Metamask", onSearchSubmit = {}, onClearInput = {}), - onBackPressed = {} - ) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Wallets.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Wallets.kt deleted file mode 100644 index 4a8bfb548..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/components/Wallets.kt +++ /dev/null @@ -1,122 +0,0 @@ -@file:OptIn(ExperimentalFoundationApi::class) - -package com.walletconnect.wcmodal.ui.components - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridItemScope -import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.wcmodal.ui.utils.imageHeaders - -@Composable -internal fun WalletsLazyGridView( - modifier: Modifier = Modifier, - content: LazyGridScope.(Int) -> Unit -) { - val walletItemWidth = 90.dp - BoxWithConstraints( - modifier = modifier - ) { - val maxColumnsNumber = maxOf((maxWidth / walletItemWidth).toInt(), 1) - LazyVerticalGrid( - columns = GridCells.Fixed(maxColumnsNumber), - content = { - content(maxColumnsNumber) - } - ) - } -} - -internal fun LazyGridScope.walletsGridItems( - wallets: List, - onWalletItemClick: (Wallet) -> Unit -) { - itemsIndexed( - items = wallets, - key = { _, wallet -> wallet.id } - ) { _, wallet -> - WalletListItem( - wallet = wallet, - onWalletItemClick = onWalletItemClick - ) - } -} - -@Composable -internal fun WalletImage(url: String, modifier: Modifier) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(url) - .crossfade(true) - .imageHeaders() - .build(), - contentDescription = null, - modifier = modifier - ) -} - -@Composable -internal fun LazyGridItemScope.WalletListItem( - wallet: Wallet, - onWalletItemClick: (Wallet) -> Unit -) { - Column( - modifier = Modifier - .animateItemPlacement() - .clip(RoundedCornerShape(20.dp)) - .clickable { onWalletItemClick(wallet) }, - horizontalAlignment = Alignment.CenterHorizontally - ) { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .size(80.dp) - .padding(10.dp) - .clip(RoundedCornerShape(14.dp)) - .border(1.dp, color = ModalTheme.colors.border, RoundedCornerShape(14.dp)) - ) - Text( - text = wallet.name, - style = TextStyle(color = ModalTheme.colors.onBackgroundColor, fontSize = 12.sp), - textAlign = TextAlign.Center - ) - Box( - modifier = Modifier.height(16.dp), - contentAlignment = Alignment.Center - ) { - when { - wallet.isRecent -> { - Text(text = "RECENT", style = TextStyle(fontSize = 10.sp, color = ModalTheme.colors.secondaryTextColor, textAlign = TextAlign.Center)) - } - wallet.isWalletInstalled -> { - Text(text = "INSTALLED", style = TextStyle(color = ModalTheme.colors.secondaryTextColor, fontSize = 10.sp)) - } - } - } - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/ModalNavGraph.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/ModalNavGraph.kt deleted file mode 100644 index 68b3b918b..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/ModalNavGraph.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.walletconnect.wcmodal.ui.navigation - -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.NavType -import androidx.navigation.navArgument -import com.google.accompanist.navigation.animation.AnimatedNavHost -import com.google.accompanist.navigation.animation.composable -import com.walletconnect.util.Empty -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.routes.all_wallets.AllWalletsRoute -import com.walletconnect.wcmodal.ui.routes.connect_wallet.ConnectYourWalletRoute -import com.walletconnect.wcmodal.ui.routes.get_wallet.GetAWalletRoute -import com.walletconnect.wcmodal.ui.routes.help.HelpRoute -import com.walletconnect.wcmodal.ui.routes.on_hold.RedirectOnHoldScreen -import com.walletconnect.wcmodal.ui.routes.scan_code.ScanQRCodeRoute - -@ExperimentalAnimationApi -@Composable -internal fun ModalNavGraph( - navController: NavHostController, - modifier: Modifier = Modifier, - viewModel: WalletConnectModalViewModel -) { - AnimatedNavHost( - navController = navController, - startDestination = Route.ConnectYourWallet.path, - modifier = modifier, - ) { - composable(route = Route.ConnectYourWallet.path) { - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - composable(route = Route.ScanQRCode.path) { - ScanQRCodeRoute(navController = navController, viewModel = viewModel) - } - composable(route = Route.Help.path) { - HelpRoute(navController = navController) - } - composable(route = Route.AllWallets.path) { - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - composable(route = Route.GetAWallet.path) { - GetAWalletRoute(navController = navController, wallets = viewModel.getNotInstalledWallets()) - } - composable( - route = Route.OnHold.path + "/" + Route.OnHold.walletIdArg, - arguments = listOf(navArgument(Route.OnHold.walletIdKey) { type = NavType.StringType }) - ) { backStackEntry -> - val walletId = backStackEntry.arguments?.getString(Route.OnHold.walletIdKey, String.Empty) - val wallet = viewModel.getWallet(walletId) - wallet?.let { RedirectOnHoldScreen(navController = navController, wallet = wallet, viewModel = viewModel).also { viewModel.saveRecentWallet(wallet) } } - } - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/Route.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/Route.kt deleted file mode 100644 index 3b63adef7..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/navigation/Route.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.walletconnect.wcmodal.ui.navigation - -internal sealed class Route(val path: String) { - - object WalletConnectModalRoot: Route("wc_modal_root") - - object ConnectYourWallet: Route("connect_wallet") - - object ScanQRCode : Route("scan_qr_code") - - object Help : Route("help") - - object GetAWallet : Route("get_a_wallet") - - object AllWallets : Route("all_wallets") - - object OnHold : Route("on_hold") { - const val walletIdKey = "walletId" - const val walletIdArg = "{walletId}" - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/preview/Preview.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/preview/Preview.kt deleted file mode 100644 index 545092ea4..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/preview/Preview.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.walletconnect.wcmodal.ui.preview - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.compose.rememberNavController -import com.walletconnect.wcmodal.ui.components.ModalRoot -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.wcmodal.ui.theme.ProvideModalThemeComposition - -@Composable -internal fun ModalPreview( - content: @Composable () -> Unit, -) { - ModalRoot( - navController = rememberNavController(), - closeModal = {} - ) { - content() - } -} - -@Composable -internal fun ComponentPreview( - content: @Composable ColumnScope.() -> Unit -) { - ProvideModalThemeComposition { - Column(modifier = Modifier.background(ModalTheme.colors.background)) { - content() - } - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/all_wallets/AllWalletsRoute.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/all_wallets/AllWalletsRoute.kt deleted file mode 100644 index b1338bb1c..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/all_wallets/AllWalletsRoute.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.all_wallets - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.rememberLazyGridState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.modal.ui.model.search.SearchState -import com.walletconnect.wcmodal.ui.components.ModalSearchTopBar -import com.walletconnect.wcmodal.ui.components.walletsGridItems -import com.walletconnect.wcmodal.ui.preview.ModalPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.modal.utils.isLandscape -import com.walletconnect.wcmodal.domain.dataStore.WalletsData -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.navigation.Route -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch - -@Composable -internal fun AllWalletsRoute( - navController: NavController, - viewModel: WalletConnectModalViewModel -) { - val walletsState by viewModel.walletsState.collectAsState() - - AllWalletsContent( - walletsData = walletsState, - searchPhrase = viewModel.searchPhrase, - onSearch = { viewModel.search(it) }, - onSearchClear = { viewModel.clearSearch() }, - onFetchNextPage = { viewModel.fetchMoreWallets() }, - onWalletItemClick = { navController.navigate(Route.OnHold.path + "/${it.id}") }, - onBackClick = { navController.popBackStack() } - ) -} - -@Composable -private fun AllWalletsContent( - walletsData: WalletsData, - searchPhrase: String, - onSearch: (String) -> Unit, - onSearchClear: () -> Unit, - onFetchNextPage: () -> Unit, - onWalletItemClick: (Wallet) -> Unit, - onBackClick: () -> Unit, -) { - val gridState = rememberLazyGridState() - val coroutineScope = rememberCoroutineScope() - val scrollToFirstItem = { coroutineScope.launch { gridState.scrollToItem(0) } } - val searchState = remember { - SearchState( - searchPhrase = searchPhrase, - onSearchSubmit = { onSearch(it).also { scrollToFirstItem() } }, - onClearInput = { onSearchClear().also { scrollToFirstItem() } } - ) - } - val gridFraction = if (isLandscape) 1f else .95f - - LaunchedEffect(gridState) { - snapshotFlow { gridState.firstVisibleItemIndex != 0 && !gridState.canScrollForward } - .distinctUntilChanged() - .filter { it } - .collect { onFetchNextPage() } - } - - Column( - modifier = Modifier - .fillMaxHeight(gridFraction) - .padding(horizontal = 4.dp), - ) { - ModalSearchTopBar( - searchState = searchState, - onBackPressed = onBackClick, - ) - if (walletsData.loadingState == LoadingState.REFRESH) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator( - strokeWidth = 4.dp, - color = ModalTheme.colors.main, - modifier = Modifier.size(24.dp) - ) - } - } else if (walletsData.wallets.isEmpty()) { - NoWalletsFoundItem() - } else { - WalletsGrid(gridState, walletsData, onWalletItemClick) - } - } -} - -@Composable -fun NoWalletsFoundItem() { - Text( - text = "No wallets found", - style = TextStyle(color = ModalTheme.colors.secondaryTextColor, fontSize = 16.sp), - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 50.dp) - ) -} - -@Composable -private fun WalletsGrid( - gridState: LazyGridState, - walletsData: WalletsData, - onWalletItemClick: (Wallet) -> Unit -) { - val color = ModalTheme.colors.background - Box { - LazyVerticalGrid( - state = gridState, - columns = GridCells.FixedSize(82.dp), - modifier = Modifier - .padding(horizontal = 10.dp) - .graphicsLayer { alpha = 0.99f } - .drawWithContent { - val colors = listOf(Color.Transparent, color) - drawContent() - drawRect( - brush = Brush.verticalGradient(colors, startY = 0f, endY = 40f), - blendMode = BlendMode.DstIn, - ) - }, - verticalArrangement = Arrangement.Center, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - walletsGridItems(walletsData.wallets, onWalletItemClick) - if (walletsData.loadingState == LoadingState.APPEND) { - loadingWalletsItems() - } - } - } -} - -private fun LazyGridScope.loadingWalletsItems() { - items(10) { - Surface( - modifier = Modifier.padding(4.dp), - color = Color.Transparent, - shape = RoundedCornerShape(16.dp) - ) { - Box( - modifier = Modifier - .width(76.dp) - .height(96.dp) - .background(Color.Transparent) - .border(width = 1.dp, color = ModalTheme.colors.secondaryBackgroundColor, shape = RoundedCornerShape(16.dp)) - ) - } - } -} - - -@Preview -@Composable -private fun AllWalletsPreview() { - val walletsData = WalletsData.empty() - ModalPreview { - AllWalletsContent(walletsData, "", {}, {}, {}, {}, {}) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/connect_wallet/ConnectYourWalletRoute.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/connect_wallet/ConnectYourWalletRoute.kt deleted file mode 100644 index 087d278f6..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/connect_wallet/ConnectYourWalletRoute.kt +++ /dev/null @@ -1,209 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.connect_wallet - -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.togetherWith -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.ui.components.common.ClickableImage -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.components.ModalTopBar -import com.walletconnect.wcmodal.ui.components.WalletImage -import com.walletconnect.wcmodal.ui.components.WalletListItem -import com.walletconnect.wcmodal.ui.components.WalletsLazyGridView -import com.walletconnect.wcmodal.ui.components.walletsGridItems -import com.walletconnect.wcmodal.ui.navigation.Route -import com.walletconnect.wcmodal.ui.preview.ModalPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.modal.utils.isLandscape -import com.walletconnect.wcmodal.ui.ErrorModalState -import com.walletconnect.wcmodal.ui.LoadingModalState -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel - -@Composable -internal fun ConnectYourWalletRoute( - navController: NavController, - viewModel: WalletConnectModalViewModel, -) { - - val uiState by viewModel.uiState.collectAsState() - AnimatedContent( - targetState = uiState, - label = "UiStateBuilder $uiState", - transitionSpec = { fadeIn() + slideInHorizontally { it / 2 } togetherWith fadeOut() } - ) { state -> - when (state) { - is UiState.Error -> ErrorModalState { viewModel.fetchInitialWallets() } - is UiState.Loading -> LoadingModalState() - is UiState.Success -> ConnectYourWalletContent( - wallets = state.data, - onWalletItemClick = { navController.navigate(Route.OnHold.path + "/${it.id}") }, - onViewAllClick = { navController.navigate(Route.AllWallets.path) }, - onScanIconClick = { navController.navigate(Route.ScanQRCode.path) } - ) - } - } -} - -@Composable -private fun ConnectYourWalletContent( - wallets: List, - onWalletItemClick: (Wallet) -> Unit, - onViewAllClick: () -> Unit, - onScanIconClick: () -> Unit, -) { - Column { - ModalTopBar(title = "Connect your wallet", endIcon = { - ClickableImage( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_scan), - tint = ModalTheme.colors.main, - contentDescription = "Scan Icon", - onClick = onScanIconClick - ) - }) - WalletsGrid( - wallets = wallets, - onWalletItemClick = onWalletItemClick, - onViewAllClick = onViewAllClick, - ) - Spacer(modifier = Modifier.height(8.dp)) - } -} - -@Composable -private fun WalletsGrid( - wallets: List, - onWalletItemClick: (Wallet) -> Unit, - onViewAllClick: () -> Unit -) { - val isLandscape = isLandscape - if (wallets.isNotEmpty()) { - WalletsLazyGridView( - modifier = Modifier.fillMaxWidth(), - ) { walletsInColumn -> - if (wallets.size <= walletsInColumn) { - walletsGridItems(wallets, onWalletItemClick) - } else { - walletsGridItemsWithViewAll( - if (isLandscape) walletsInColumn else walletsInColumn * 2, - wallets, - onWalletItemClick, - onViewAllClick - ) - } - } - } else { - NoWalletsFoundItem() - } -} - -@Composable -private fun NoWalletsFoundItem() { - Text( - text = "No wallets found", - style = TextStyle(color = ModalTheme.colors.secondaryTextColor, fontSize = 16.sp), - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 50.dp) - ) -} - -private fun LazyGridScope.walletsGridItemsWithViewAll( - maxGridElementsSize: Int, - wallets: List, - onWalletItemClick: (Wallet) -> Unit, - onViewAllClick: () -> Unit -) { - val walletsSize = maxGridElementsSize - 1 - itemsIndexed( - wallets.take(walletsSize), - key = { _, wallet -> wallet.id } - ) { _, wallet -> - WalletListItem( - wallet = wallet, - onWalletItemClick = onWalletItemClick - ) - } - item { - ViewAllItem(wallets.subList(walletsSize, wallets.size), onViewAllClick) - } -} - -@Composable -private fun ViewAllItem( - wallets: List, - onViewAllClick: () -> Unit -) { - Column( - modifier = Modifier - .clip(RoundedCornerShape(20.dp)) - .clickable { onViewAllClick() }, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Column( - modifier = Modifier - .size(80.dp) - .padding(10.dp) - .background(ModalTheme.colors.secondaryBackgroundColor, shape = RoundedCornerShape(14.dp)) - .border(1.dp, ModalTheme.colors.dividerColor, shape = RoundedCornerShape(14.dp)) - .padding(1.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - wallets.chunked(2).forEach { - Row { - it.forEach { item -> - WalletImage( - url = item.imageUrl, Modifier - .size(30.dp) - .padding(5.dp) - .clip(RoundedCornerShape(4.dp)) - ) - } - } - } - - } - Text(text = "View All", style = TextStyle(color = ModalTheme.colors.onBackgroundColor, fontSize = 12.sp)) - } -} - -@Preview -@Composable -private fun ConnectYourWalletPreview() { - ModalPreview { - ConnectYourWalletContent(listOf(), {}, {}, {}) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/get_wallet/GetAWalletRoute.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/get_wallet/GetAWalletRoute.kt deleted file mode 100644 index 9efeb3116..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/get_wallet/GetAWalletRoute.kt +++ /dev/null @@ -1,160 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.get_wallet - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.utils.openPlayStore -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.components.ModalTopBar -import com.walletconnect.wcmodal.ui.components.RoundedMainButton -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.wcmodal.ui.utils.imageHeaders - -@Composable -internal fun GetAWalletRoute( - navController: NavController, - wallets: List, -) { - GetAWalletContent( - wallets = wallets, - onBackPressed = navController::popBackStack, - ) -} - -@Composable -private fun GetAWalletContent( - onBackPressed: () -> Unit, - wallets: List, -) { - val uriHandler = LocalUriHandler.current - - Column { - ModalTopBar(title = "Get a wallet", onBackPressed = onBackPressed) - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - ) { - itemsIndexed(wallets.take(6)) { _, wallet -> - WalletListItem(wallet = wallet) - } - item { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp, horizontal = 12.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = "Not what you're looking for?", style = TextStyle( - color = ModalTheme.colors.textColor, - textAlign = TextAlign.Center, - fontSize = 18.sp - ) - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = "With hundreds of wallets out there, there’s something for everyone", - style = TextStyle( - color = ModalTheme.colors.secondaryTextColor, - textAlign = TextAlign.Center, - fontSize = 14.sp - ) - ) - Spacer(modifier = Modifier.height(6.dp)) - RoundedMainButton( - text = "Explore Wallets", - onClick = { uriHandler.openUri("https://explorer.walletconnect.com/?type=wallet") }, - endIcon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_external_link), - colorFilter = ColorFilter.tint(ModalTheme.colors.onMainColor), - contentDescription = null, - ) - } - ) - } - } - } - } -} - -@Composable -private fun WalletListItem(wallet: Wallet) { - val uriHandler = LocalUriHandler.current - Column { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 6.dp), - verticalAlignment = Alignment.CenterVertically - ) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(wallet.imageUrl) - .crossfade(true) - .imageHeaders() - .build(), - contentDescription = null, - modifier = Modifier - .size(50.dp) - .clip(RoundedCornerShape(10.dp)) - ) - Spacer(modifier = Modifier.width(12.dp)) - Text( - text = wallet.name, - style = TextStyle( - fontSize = 16.sp, - color = ModalTheme.colors.onBackgroundColor, - ), - modifier = Modifier.weight(1f) - ) - RoundedMainButton( - text = "Get", - onClick = { uriHandler.openPlayStore(wallet.playStore) }, - endIcon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_forward_chevron), - colorFilter = ColorFilter.tint(ModalTheme.colors.onMainColor), - contentDescription = null, - ) - } - ) - } - Box( - modifier = Modifier - .height(1.dp) - .fillMaxWidth() - .background(ModalTheme.colors.dividerColor) - ) - } -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/help/HelpRoute.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/help/HelpRoute.kt deleted file mode 100644 index 47fe2570f..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/help/HelpRoute.kt +++ /dev/null @@ -1,168 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.help - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.navigation.NavController -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.ui.components.ModalTopBar -import com.walletconnect.wcmodal.ui.components.RoundedMainButton -import com.walletconnect.wcmodal.ui.navigation.Route -import com.walletconnect.wcmodal.ui.preview.ModalPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme - -@Composable -internal fun HelpRoute( - navController: NavController -) { - HelpContent( - onBackPressed = navController::popBackStack, - onGetWalletClick = { navController.navigate(Route.GetAWallet.path) } - ) -} - -@Composable -private fun HelpContent( - onBackPressed: () -> Unit, - onGetWalletClick: () -> Unit, -) { - Column(modifier = Modifier.padding(horizontal = 20.dp)) { - ModalTopBar( - title = "What is wallet?", - onBackPressed = onBackPressed - ) - Column(modifier = Modifier.padding(horizontal = 32.dp)) { - HelpSection( - title = "A home for your digital assets", - body = "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", - assets = listOf(R.drawable.defi, R.drawable.nft, R.drawable.eth) - ) - VerticalSpacer(4.dp) - HelpSection( - title = "One login for all of web3", - body = "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", - assets = listOf(R.drawable.login, R.drawable.profile, R.drawable.lock) - ) - VerticalSpacer(4.dp) - HelpSection( - title = "Your gateway to a new web", - body = "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", - assets = listOf(R.drawable.browser, R.drawable.noun, R.drawable.dao) - ) - VerticalSpacer(4.dp) - HelpButtonRow(onGetWalletClick) - VerticalSpacer(20.dp) - } - } -} - -@Composable -private fun HelpButtonRow( - onGetWalletClick: () -> Unit, -) { - val uriHandler = LocalUriHandler.current - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - RoundedMainButton( - text = "Get a Wallet", - onClick = onGetWalletClick, - startIcon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet), - contentDescription = "Wallet icon", - modifier = Modifier.size(14.dp), - colorFilter = ColorFilter.tint(color = ModalTheme.colors.onMainColor) - ) - } - ) - HorizontalSpacer(10.dp) - RoundedMainButton( - text = "Learn More", - onClick = { - uriHandler.openUri("https://ethereum.org/en/wallets/") - }, - endIcon = { - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_external_link), - contentDescription = "external link icon", - modifier = Modifier.size(14.dp), - colorFilter = ColorFilter.tint(color = ModalTheme.colors.onMainColor) - ) - } - ) - } -} - -@Composable -private fun HelpSection( - title: String, - body: String, - assets: List -) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Row { - assets.forEach { vectorRes -> - Image( - imageVector = ImageVector.vectorResource(id = vectorRes), - contentDescription = null, - modifier = Modifier - .padding(horizontal = 5.dp) - .size(60.dp) - ) - } - } - VerticalSpacer(4.dp) - Text( - text = title, - style = TextStyle( - fontSize = 16.sp, - color = ModalTheme.colors.textColor, - textAlign = TextAlign.Center - ) - ) - VerticalSpacer(4.dp) - Text( - text = body, - style = TextStyle( - fontSize = 14.sp, - color = ModalTheme.colors.secondaryTextColor, - textAlign = TextAlign.Center - ) - ) - } -} - -@Composable -@Preview -private fun HelpContentPreview() { - ModalPreview { - HelpContent({}, {}) - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectOnHoldScreen.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectOnHoldScreen.kt deleted file mode 100644 index a6bbdca83..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectOnHoldScreen.kt +++ /dev/null @@ -1,305 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.on_hold - -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.RepeatMode -import androidx.compose.animation.core.animateFloat -import androidx.compose.animation.core.infiniteRepeatable -import androidx.compose.animation.core.rememberInfiniteTransition -import androidx.compose.animation.core.tween -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Divider -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.KeyboardArrowRight -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Rect -import androidx.compose.ui.geometry.RoundRect -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.PathMeasure -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.ui.components.common.HorizontalSpacer -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.modal.ui.components.common.WeightSpacer -import com.walletconnect.modal.utils.goToNativeWallet -import com.walletconnect.modal.utils.openPlayStore -import com.walletconnect.modal.utils.openWebAppLink -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.domain.WalletConnectModalDelegate -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.components.ModalTopBar -import com.walletconnect.wcmodal.ui.components.RoundedMainButton -import com.walletconnect.wcmodal.ui.components.WalletImage -import com.walletconnect.wcmodal.ui.preview.ModalPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme - -@Composable -internal fun RedirectOnHoldScreen( - navController: NavController, - wallet: Wallet, - viewModel: WalletConnectModalViewModel -) { - val uriHandler = LocalUriHandler.current - val redirectState = remember { mutableStateOf(RedirectState.Loading) } - - LaunchedEffect(Unit) { - WalletConnectModalDelegate - .wcEventModels - .collect { - when (it) { - is Modal.Model.RejectedSession -> redirectState.value = RedirectState.Reject - is Modal.Model.ExpiredProposal -> redirectState.value = RedirectState.Expired - else -> Unit - } - } - } - - RedirectOnHoldScreen( - wallet = wallet, - state = redirectState.value, - onBackPressed = navController::popBackStack, - onRetry = { - viewModel.connect { uri -> - redirectState.value = RedirectState.Loading - uriHandler.goToNativeWallet(uri, wallet.mobileLink) - } - }, - onOpenWebLink = { - viewModel.connect { uri -> - uriHandler.openWebAppLink(uri, wallet.webAppLink) - } - }, - onOpenPlayStore = { uriHandler.openPlayStore(wallet.playStore) } - ) - - LaunchedEffect(Unit) { - viewModel.connect { uri -> - uriHandler.goToNativeWallet(uri, wallet.mobileLink) - } - } -} - -@Composable -private fun RedirectOnHoldScreen( - wallet: Wallet, - state: RedirectState, - onBackPressed: () -> Unit, - onRetry: () -> Unit, - onOpenWebLink: () -> Unit, - onOpenPlayStore: () -> Unit -) { - Column( - modifier = Modifier.verticalScroll(rememberScrollState()) - ) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - ModalTopBar( - title = wallet.name, - onBackPressed = onBackPressed - ) - VerticalSpacer(height = 20.dp) - RedirectStateContent(state = state, wallet = wallet) - VerticalSpacer(height = 20.dp) - } - BottomSection(wallet, onRetry, onOpenWebLink, onOpenPlayStore) - } -} - -@Composable -private fun RedirectStateContent(state: RedirectState, wallet: Wallet) { - when (state) { - RedirectState.Loading -> { - WalletImageWithLoader(wallet.imageUrl) - VerticalSpacer(height = 20.dp) - Text(text = "Continue in ${wallet.name}...", color = ModalTheme.colors.textColor) - } - - RedirectState.Reject -> { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .border(1.dp, ModalTheme.colors.errorColor, shape = RoundedCornerShape(22.dp)) - .size(90.dp) - .padding(4.dp) - .clip(RoundedCornerShape(20.dp)) - ) - VerticalSpacer(height = 20.dp) - Text(text = "Connection declined", color = ModalTheme.colors.errorColor) - } - - RedirectState.Expired -> { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .border(1.dp, ModalTheme.colors.errorColor, shape = RoundedCornerShape(22.dp)) - .size(90.dp) - .padding(4.dp) - .clip(RoundedCornerShape(20.dp)) - ) - VerticalSpacer(height = 20.dp) - Text(text = "Connection expired", color = ModalTheme.colors.errorColor) - } - } -} - -@Composable -private fun WalletImageWithLoader(imageUrl: String) { - val mainColor = ModalTheme.colors.main - val infiniteTransition = rememberInfiniteTransition() - - val value by infiniteTransition.animateFloat( - initialValue = 100f, targetValue = 0f, animationSpec = infiniteRepeatable( - animation = tween(1500, easing = LinearEasing), - repeatMode = RepeatMode.Restart - ) - ) - val pathWithProgress by remember { mutableStateOf(Path()) } - val pathMeasure by remember { mutableStateOf(PathMeasure()) } - val path = remember { Path() } - - - WalletImage( - url = imageUrl, - modifier = Modifier - .size(90.dp) - .drawBehind { - if (path.isEmpty) { - path.addRoundRect( - RoundRect( - Rect(offset = Offset.Zero, size), - cornerRadius = CornerRadius(20.dp.toPx(), 20.dp.toPx()) - ) - ) - } - pathWithProgress.reset() - pathWithProgress.rewind() - pathMeasure.setPath(path, forceClosed = true) - pathMeasure.getSegment( - startDistance = pathMeasure.length * value / 100, - stopDistance = pathMeasure.length * value / 100 + minOf(250f, value * 10), - pathWithProgress, - startWithMoveTo = true - ) - drawPath( - path = pathWithProgress, - style = Stroke( - 3.dp.toPx() - ), - color = mainColor - ) - } - .padding(4.dp) - .clip(RoundedCornerShape(20.dp)) - ) -} - -@Composable -private fun BottomSection( - wallet: Wallet, - onRetry: () -> Unit, - onOpenUniversalLink: () -> Unit, - onOpenPlayStore: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .background(ModalTheme.colors.secondaryBackgroundColor) - .padding(vertical = 10.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - RoundedMainButton( - text = "Retry", - onClick = { onRetry() }, - endIcon = { - Image(imageVector = ImageVector.vectorResource(id = R.drawable.ic_retry), contentDescription = null) - } - ) - VerticalSpacer(height = 10.dp) - if (wallet.webAppLink != null) { - Row(Modifier.padding(horizontal = 20.dp, vertical = 6.dp)) { - Text(text = "Still doesn't work?", color = ModalTheme.colors.secondaryTextColor) - HorizontalSpacer(width = 2.dp) - Text(text = "Try this alternate link", color = ModalTheme.colors.main, modifier = Modifier.clickable { onOpenUniversalLink() }) - } - } - Divider( - modifier = Modifier - .padding(vertical = 10.dp) - .fillMaxWidth(), color = ModalTheme.colors.dividerColor - ) - PlayStoreRow(wallet, onOpenPlayStore) - } -} - -@Composable -private fun PlayStoreRow(wallet: Wallet, onOpenPlayStore: () -> Unit) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - .clickable { onOpenPlayStore() }, - verticalAlignment = Alignment.CenterVertically, - ) { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .size(28.dp) - .clip(RoundedCornerShape(4.dp)) - ) - HorizontalSpacer(width = 6.dp) - Text(text = "Get ${wallet.name}", color = ModalTheme.colors.textColor) - WeightSpacer() - Text(text = "Play Store", color = ModalTheme.colors.secondaryTextColor) - Image(imageVector = Icons.Default.KeyboardArrowRight, contentDescription = null, colorFilter = ColorFilter.tint(ModalTheme.colors.secondaryTextColor)) - } -} - -@Preview -@Composable -private fun OnHoldScreenPreview( - @PreviewParameter(RedirectStateProvider::class) state: RedirectState -) { - ModalPreview { - val wallet = Wallet("Id", "Kotlin Wallet", "url", "", "", "", null, "", linkMode = "", false) - RedirectOnHoldScreen(wallet = wallet, state = state, onBackPressed = { }, onRetry = { }, onOpenWebLink = { }, onOpenPlayStore = {}) - } -} - -private class RedirectStateProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf(RedirectState.Loading, RedirectState.Reject, RedirectState.Expired) -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectState.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectState.kt deleted file mode 100644 index c80b124d5..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/on_hold/RedirectState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.on_hold - -internal sealed class RedirectState { - object Loading : RedirectState() - object Reject : RedirectState() - object Expired: RedirectState() -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/scan_code/ScanCodeRoute.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/scan_code/ScanCodeRoute.kt deleted file mode 100644 index a2aac1e76..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/routes/scan_code/ScanCodeRoute.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.scan_code - -import android.widget.Toast -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.ClipboardManager -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.modal.ui.components.common.ClickableImage -import com.walletconnect.modal.ui.components.qr.QrCodeType -import com.walletconnect.modal.ui.components.qr.WalletConnectQRCode -import com.walletconnect.modal.utils.isLandscape -import com.walletconnect.wcmodal.R -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.domain.WalletConnectModalDelegate -import com.walletconnect.wcmodal.ui.LoadingModalState -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.components.ModalTopBar -import com.walletconnect.wcmodal.ui.preview.ModalPreview -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import kotlinx.coroutines.flow.filterIsInstance - -@Composable -internal fun ScanQRCodeRoute( - navController: NavController, - viewModel: WalletConnectModalViewModel -) { - val context = LocalContext.current - var uri by remember { mutableStateOf("") } - - LaunchedEffect(Unit) { - WalletConnectModalDelegate - .wcEventModels - .filterIsInstance() - .collect { - Toast.makeText(context, "Declined", Toast.LENGTH_SHORT).show() - viewModel.connect { newUri -> uri = newUri } - } - } - - LaunchedEffect(Unit) { - viewModel.connect { uri = it } - } - - ScanQRCodeContent( - uri = uri, - onBackArrowClick = navController::popBackStack - ) -} - -@Composable -private fun ScanQRCodeContent( - uri: String, - onBackArrowClick: () -> Unit, -) { - val context = LocalContext.current - val clipboardManager: ClipboardManager = LocalClipboardManager.current - val qrCodeModifier = if (isLandscape) Modifier else Modifier.padding(horizontal = 20.dp) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.animateContentSize() - ) { - ModalTopBar( - title = "Scan the code", - onBackPressed = onBackArrowClick, - endIcon = { - ClickableImage( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_copy), - tint = ModalTheme.colors.main, - contentDescription = "Scan Icon", - onClick = { - Toast.makeText(context, "Link copied", Toast.LENGTH_SHORT).show() - clipboardManager.setText(AnnotatedString(uri)) - } - ) - } - ) - if (uri.isNotBlank()) { - WalletConnectQRCode( - qrData = uri, - primaryColor = ModalTheme.colors.onBackgroundColor, - logoColor = ModalTheme.colors.main, - type = QrCodeType.WCM, - modifier = qrCodeModifier - ) - } else { - LoadingModalState() - } - Spacer(modifier = Modifier.height(8.dp)) - } -} - -@Preview -@Composable -private fun ScanQRCodePreview() { - ModalPreview { - ScanQRCodeContent("47442c19ea7c6a7a836fa3e53af1ddd375438daaeea9acdbf595e989a731b73249a10a7cc0e343ca627e536609", {}) - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/state/ModalState.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/state/ModalState.kt deleted file mode 100644 index edb9f00cc..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/state/ModalState.kt +++ /dev/null @@ -1,37 +0,0 @@ -@file:OptIn(ExperimentalCoroutinesApi::class) - -package com.walletconnect.wcmodal.ui.state - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.navigation.NavController -import androidx.navigation.NavDestination -import com.walletconnect.wcmodal.ui.navigation.Route -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.mapLatest - -@Composable -fun rememberModalState( - navController: NavController -): ModalState = remember(navController) { - ModalState(navController) -} - -class ModalState( - private val navController: NavController -) { - val isOpen: Boolean - get() = navController.currentDestination.isModal() - - val isOpenFlow - get() = navController.currentBackStackEntryFlow.mapLatest { it.destination.isModal() } - - fun closeModal() { - if(isOpen) { - navController.popBackStack() - } - } - - private fun NavDestination?.isModal() = - this?.route?.startsWith("${Route.WalletConnectModalRoot.path}?") ?: false -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Composition.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Composition.kt deleted file mode 100644 index 93de5b036..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Composition.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.wcmodal.ui.theme - -import androidx.compose.runtime.compositionLocalOf -import androidx.compose.ui.graphics.Color - -internal data class CustomizableComposition( - val accentColor: Color = Color(0xFF3496ff), - val onAccentColor: Color = Color.White -) - -internal val LocalCustomComposition = compositionLocalOf { CustomizableComposition() } \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/ModalColors.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/ModalColors.kt deleted file mode 100644 index f4be7078c..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/ModalColors.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.walletconnect.wcmodal.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color - -internal data class ModalColors( - val main: Color, - val onMainColor: Color, - val background: Color, - val onBackgroundColor: Color, - val secondaryBackgroundColor: Color, - val textColor: Color, - val secondaryTextColor: Color, - val dividerColor: Color, - val border: Color, - val errorColor: Color = Color(0xFFF05142) -) - -@Composable -internal fun provideModalColors( - composition: CustomizableComposition -): ModalColors = if (isSystemInDarkTheme()) { - defaultDarkWeb3ModalColors(composition.accentColor, composition.onAccentColor) -} else { - defaultLightWeb3ModalColors(composition.accentColor, composition.onAccentColor) -} - -private fun defaultLightWeb3ModalColors( - mainColor: Color, - onMainColor: Color -) = ModalColors( - main = mainColor, - onMainColor = onMainColor, - onBackgroundColor = Color.Black, - background = Color.White, - textColor = Color.Black, - secondaryTextColor = Color(0xFF798686), - secondaryBackgroundColor = Color(0xFFF1F3F3), - dividerColor = Color(0xFFE4E7E7), - border = Color(0x32062B2B) -) - -private fun defaultDarkWeb3ModalColors( - mainColor: Color, - onMainColor: Color -) = ModalColors( - main = mainColor, - onMainColor = onMainColor, - background = Color.Black, - onBackgroundColor = Color.White, - textColor = Color.White, - secondaryTextColor = Color(0xFF949E9E), - secondaryBackgroundColor = Color(0xFF272A2A), - dividerColor = Color(0xFF3B4040), - border = Color(0x32062B2B) -) diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Theme.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Theme.kt deleted file mode 100644 index d6aaaf6a7..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/theme/Theme.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.walletconnect.wcmodal.ui.theme - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.compositionLocalOf -import androidx.compose.ui.graphics.Color - -@Composable -fun WalletConnectModalTheme( - accentColor: Color = Color(0xFF3496ff), - onAccentColor: Color = Color.White, - content: @Composable () -> Unit -) { - val customComposition = CustomizableComposition( - accentColor = accentColor, - onAccentColor = onAccentColor - ) - CompositionLocalProvider( - LocalCustomComposition provides customComposition - ) { - content() - } -} - -@Composable -internal fun ProvideModalThemeComposition(content: @Composable () -> Unit) { - val composition = LocalCustomComposition.current - - val colors = provideModalColors(composition) - CompositionLocalProvider( - LocalColorsComposition provides colors, - content = content - ) -} - -internal object ModalTheme { - val colors: ModalColors - @Composable - get() = LocalColorsComposition.current -} - -private val LocalColorsComposition = compositionLocalOf { - error("No colors provided") -} diff --git a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/utils/Image.kt b/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/utils/Image.kt deleted file mode 100644 index 2799eb8c3..000000000 --- a/product/walletconnectmodal/src/main/kotlin/com/walletconnect/wcmodal/ui/utils/Image.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.wcmodal.ui.utils - -import coil.request.ImageRequest -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp - -internal fun ImageRequest.Builder.imageHeaders() = apply { - addHeader("x-project-id", wcKoinApp.koin.get().value) - addHeader("x-sdk-version", BuildConfig.SDK_VERSION) - addHeader("x-sdk-type", "wcm") -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/res/drawable/chevron_left.xml b/product/walletconnectmodal/src/main/res/drawable/chevron_left.xml deleted file mode 100644 index 755a53eca..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/chevron_left.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_close.xml b/product/walletconnectmodal/src/main/res/drawable/ic_close.xml deleted file mode 100644 index ee696aef5..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_close.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_copy.xml b/product/walletconnectmodal/src/main/res/drawable/ic_copy.xml deleted file mode 100644 index 7bafb64c6..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_copy.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_external_link.xml b/product/walletconnectmodal/src/main/res/drawable/ic_external_link.xml deleted file mode 100644 index 33187d09d..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_external_link.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_forward_chevron.xml b/product/walletconnectmodal/src/main/res/drawable/ic_forward_chevron.xml deleted file mode 100644 index 73e54a7cd..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_forward_chevron.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_question_mark.xml b/product/walletconnectmodal/src/main/res/drawable/ic_question_mark.xml deleted file mode 100644 index cd605464b..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_question_mark.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_retry.xml b/product/walletconnectmodal/src/main/res/drawable/ic_retry.xml deleted file mode 100644 index 20b160d63..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_retry.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_search.xml b/product/walletconnectmodal/src/main/res/drawable/ic_search.xml deleted file mode 100644 index 58b5434e9..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_search.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/drawable/ic_wallet.xml b/product/walletconnectmodal/src/main/res/drawable/ic_wallet.xml deleted file mode 100644 index 9bd8e3b41..000000000 --- a/product/walletconnectmodal/src/main/res/drawable/ic_wallet.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/product/walletconnectmodal/src/main/res/values/attrs.xml b/product/walletconnectmodal/src/main/res/values/attrs.xml deleted file mode 100644 index 6b67338e2..000000000 --- a/product/walletconnectmodal/src/main/res/values/attrs.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/product/walletconnectmodal/src/main/res/values/theme.xml b/product/walletconnectmodal/src/main/res/values/theme.xml deleted file mode 100644 index 1fac6090f..000000000 --- a/product/walletconnectmodal/src/main/res/values/theme.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModelTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModelTest.kt deleted file mode 100644 index 8112b2f7f..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/WalletConnectModalViewModelTest.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.walletconnect.wcmodal.ui - -import app.cash.turbine.test -import com.walletconnect.android.CoreClient -import com.walletconnect.android.internal.common.modal.data.model.WalletListing -import com.walletconnect.android.internal.common.modal.domain.usecase.GetInstalledWalletsIdsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetSampleWalletsUseCaseInterface -import com.walletconnect.android.internal.common.modal.domain.usecase.GetWalletsUseCaseInterface -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.foundation.util.Logger -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import com.walletconnect.wcmodal.domain.usecase.GetRecentWalletUseCase -import com.walletconnect.wcmodal.domain.usecase.SaveRecentWalletUseCase -import com.walletconnect.wcmodal.utils.testWallets -import io.mockk.coEvery -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.unmockkObject -import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.koin.core.Koin -import org.koin.core.KoinApplication - -class WalletConnectModalViewModelTest { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val koinApp: KoinApplication = mockk() - private val koin: Koin = mockk() - private val getWalletsUseCase: GetWalletsUseCaseInterface = mockk() - private val getRecentWalletUseCase: GetRecentWalletUseCase = mockk() - private val saveRecentWalletUseCase: SaveRecentWalletUseCase = mockk() - private val getWalletsAppDataUseCase: GetInstalledWalletsIdsUseCaseInterface = mockk() - private val getSampleWalletsUseCaseInterface: GetSampleWalletsUseCaseInterface = mockk() - private val logger: Logger = mockk() - - private val sessionParams = Modal.Params.SessionParams( - mapOf( - Pair("eip155", Modal.Model.Namespace.Proposal(chains = listOf("eip155:1"), methods = listOf(), events = listOf())) - ), - null, - null - ) - - @Before - fun setup() { - mockkStatic("com.walletconnect.android.internal.common.KoinApplicationKt") - every { wcKoinApp } returns koinApp - every { koinApp.koin } returns koin - every { koin.get() } returns getWalletsUseCase - every { koin.get() } returns getRecentWalletUseCase - every { koin.get() } returns saveRecentWalletUseCase - every { koin.get() } returns getWalletsAppDataUseCase - every { koin.get() } returns getSampleWalletsUseCaseInterface - every { koin.get() } returns logger - every { getRecentWalletUseCase() } returns null - - coEvery { getWalletsAppDataUseCase.invoke("wcm") } returns listOf() - WalletConnectModal.setSessionParams(sessionParams) - } - - @After - fun after() { - unmockkObject(CoreClient) - unmockkObject(WalletConnectModal) - } - - @Test - fun `should emit Error state when fetch initial wallets fails`() = runTest { - every { logger.error(any()) } answers {} - every { logger.error(any()) } answers {} - - val viewModel = WalletConnectModalViewModel() - viewModel.uiState.test { - val state = awaitItem() - Assert.assertTrue(state is UiState.Error) - } - } - - @Test - fun `should emit Success state when fetch initial wallets with success`() = runTest { - val response = WalletListing(1, testWallets.size, testWallets) - coEvery { getWalletsUseCase(any(), any(), any(), any(), any()) } returns response - coEvery { getSampleWalletsUseCaseInterface() } returns listOf() - - val viewModel = WalletConnectModalViewModel() - viewModel.uiState.test { - val state = awaitItem() - Assert.assertTrue(state is UiState.Success) - } - } -} diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/AllWalletsRouteTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/AllWalletsRouteTest.kt deleted file mode 100644 index 984d1940d..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/AllWalletsRouteTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.snapshots - -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.wcmodal.domain.dataStore.WalletsData -import com.walletconnect.wcmodal.ui.MainDispatcherRule -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.routes.all_wallets.AllWalletsRoute -import com.walletconnect.wcmodal.utils.ScreenShotTest -import com.walletconnect.wcmodal.utils.testWallets -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.koin.core.Koin -import org.koin.core.KoinApplication - -class AllWalletsRouteTest: ScreenShotTest() { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val navController: NavController = mockk() - private val viewModel: WalletConnectModalViewModel = mockk() - private val koinApp: KoinApplication = mockk() - private val koin: Koin = mockk() - - @Before - fun setup() { - mockkStatic("com.walletconnect.android.internal.common.KoinApplicationKt") - every { wcKoinApp } returns koinApp - every { koinApp.koin } returns koin - every { koin.get() } returns ProjectId("test-projectId") - every { viewModel.fetchInitialWallets() } returns Unit - every { viewModel.searchPhrase } returns "" - } - - @Test - fun `test AllWalletsRoute in LightMode with empty list of wallets`() = runScreenShotTest { - every { viewModel.walletsState.value } returns WalletsData(listOf()) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute in DarkMode with empty list of wallets`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.walletsState.value } returns WalletsData(listOf()) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute in LightMode with 3 wallets`() = runScreenShotTest { - every { viewModel.walletsState.value } returns WalletsData(testWallets.take(3)) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute in DarkMode with 3 wallets`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.walletsState.value } returns WalletsData(testWallets.take(3)) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute in LightMode whole wallets list`() = runScreenShotTest { - every { viewModel.walletsState.value } returns WalletsData(testWallets) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute in DarkMode with whole wallets list`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.walletsState.value } returns WalletsData(testWallets) - AllWalletsRoute(navController = navController, viewModel = viewModel) - } -} diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ConnectYourWalletRouteTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ConnectYourWalletRouteTest.kt deleted file mode 100644 index 5745505e2..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ConnectYourWalletRouteTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.snapshots - -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.wcmodal.ui.MainDispatcherRule -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.routes.connect_wallet.ConnectYourWalletRoute -import com.walletconnect.wcmodal.utils.ScreenShotTest -import com.walletconnect.wcmodal.utils.testWallets -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.koin.core.Koin -import org.koin.core.KoinApplication - -class ConnectYourWalletRouteTest: ScreenShotTest() { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val navController: NavController = mockk() - private val viewModel: WalletConnectModalViewModel = mockk() - private val koinApp: KoinApplication = mockk() - private val koin: Koin = mockk() - - @Before - fun setup() { - mockkStatic("com.walletconnect.android.internal.common.KoinApplicationKt") - every { wcKoinApp } returns koinApp - every { koinApp.koin } returns koin - every { koin.get() } returns ProjectId("test-projectId") - every { viewModel.fetchInitialWallets() } returns Unit - } - - @Test - fun `test ConnectYourWalletRoute in LightMode with empty list of wallets`() = runScreenShotTest { - every { viewModel.uiState.value } returns UiState.Success(listOf()) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ConnectYourWalletRoute in DarkMode with empty list of wallets`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.uiState.value } returns UiState.Success(listOf()) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ConnectYourWalletRoute in LightMode with 3 wallets`() = runScreenShotTest { - every { viewModel.uiState.value } returns UiState.Success(testWallets.take(3)) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ConnectYourWalletRoute in DarkMode with 3 wallets`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.uiState.value } returns UiState.Success(testWallets.take(3)) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ConnectYourWalletRoute in LightMode with whole wallets`() = runScreenShotTest { - every { viewModel.uiState.value } returns UiState.Success(testWallets) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ConnectYourWalletRoute in DarkMode with whole wallets`() = runScreenShotTest(isDarkMode = true) { - every { viewModel.uiState.value } returns UiState.Success(testWallets) - ConnectYourWalletRoute(navController = navController, viewModel = viewModel) - } -} diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/RedirectOnHoldRouteTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/RedirectOnHoldRouteTest.kt deleted file mode 100644 index 1119e3e69..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/RedirectOnHoldRouteTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.snapshots - -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.wcmodal.ui.MainDispatcherRule -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.routes.on_hold.RedirectOnHoldScreen -import com.walletconnect.wcmodal.utils.ScreenShotTest -import com.walletconnect.wcmodal.utils.testWallets -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.koin.core.Koin -import org.koin.core.KoinApplication - -class RedirectOnHoldRouteTest: ScreenShotTest() { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val navController: NavController = mockk() - private val viewModel: WalletConnectModalViewModel = mockk() - private val koinApp: KoinApplication = mockk() - private val koin: Koin = mockk() - - @Before - fun setup() { - mockkStatic("com.walletconnect.android.internal.common.KoinApplicationKt") - every { wcKoinApp } returns koinApp - every { koinApp.koin } returns koin - every { koin.get() } returns ProjectId("test-projectId") - every { viewModel.fetchInitialWallets() } returns Unit - } - @Test - fun `test RedirectOnHoldRoute in LightMode`() = runScreenShotTest { - RedirectOnHoldScreen(navController = navController, wallet = testWallets.first(), viewModel = viewModel) - } - - @Test - fun `test RedirectOnHoldRoute in DarkMode`() = runScreenShotTest(isDarkMode = true) { - RedirectOnHoldScreen(navController = navController, wallet = testWallets.first(), viewModel = viewModel) - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ScanCodeRouteTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ScanCodeRouteTest.kt deleted file mode 100644 index 09546fa9f..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/ui/routes/snapshots/ScanCodeRouteTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.wcmodal.ui.routes.snapshots - -import androidx.compose.runtime.Composable -import androidx.navigation.NavController -import com.walletconnect.wcmodal.ui.MainDispatcherRule -import com.walletconnect.wcmodal.ui.WalletConnectModalViewModel -import com.walletconnect.wcmodal.ui.routes.scan_code.ScanQRCodeRoute -import com.walletconnect.wcmodal.utils.ScreenShotTest -import io.mockk.every -import io.mockk.mockk -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -class ScanCodeRouteTest : ScreenShotTest() { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val navController: NavController = mockk() - private val viewModel: WalletConnectModalViewModel = mockk() - - @Before - fun setup() { - every { viewModel.fetchInitialWallets() } returns Unit - } - - private val content: @Composable () -> Unit = { - ScanQRCodeRoute(navController = navController, viewModel = viewModel) - } - - @Test - fun `test ScanQRCodeRoute in LightMode`() = runScreenShotTest { content() } - - @Test - fun `test ScanQRCodeRoute in DarkMode`() = runScreenShotTest(isDarkMode = true, content = content) -} diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/DataProvider.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/DataProvider.kt deleted file mode 100644 index 469314aaa..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/DataProvider.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.wcmodal.utils - -import com.walletconnect.android.internal.common.modal.data.model.Wallet - -private val metaMask: Wallet - get() = Wallet(id = "1", name = "MetaMask", homePage = "", order = "", imageUrl = "", mobileLink = "metamask://", webAppLink = "", playStore = "", linkMode = "") -private val trustWallet: Wallet - get() = Wallet(id = "2", name = "Trust Wallet", homePage = "", order = "", imageUrl = "", mobileLink = "trustwallet://", webAppLink = "", playStore = "", linkMode = "") -private val safe: Wallet - get() = Wallet(id = "3", name = "Safe", homePage = "", order = "", imageUrl = "", mobileLink = "safe://", webAppLink = "", playStore = "", linkMode = "") -private val rainbow: Wallet - get() = Wallet(id = "4", name = "Rainbow", homePage = "", order = "", imageUrl = "", mobileLink = "rainbow://", webAppLink = "", playStore = "", linkMode = "") -private val zerion: Wallet - get() = Wallet(id = "5", name = "Zerion", homePage = "", order = "", imageUrl = "", mobileLink = "zerion://", webAppLink = "", playStore = "", linkMode = "") -private val argent: Wallet - get() = Wallet(id = "6", name = "Argent", homePage = "", order = "", imageUrl = "", mobileLink = "argent://", webAppLink = "", playStore = "", linkMode = "") -private val spot: Wallet - get() = Wallet(id = "7", name = "Spot", homePage = "", order = "", imageUrl = "", mobileLink = "spot://", webAppLink = "", playStore = "", linkMode = "") -private val imToken: Wallet - get() = Wallet(id = "8", name = "imToken", homePage = "", order = "", imageUrl = "", mobileLink = "imtoken://", webAppLink = "", playStore = "", linkMode = "") -private val alphaWallet: Wallet - get() = Wallet(id = "9", name = "AlphaWallet", homePage = "", order = "", imageUrl = "", mobileLink = "alphawallet://", webAppLink = "", playStore = "", linkMode = "") -private val omni: Wallet - get() = Wallet(id = "10", name = "Omni", homePage = "", order = "", imageUrl = "", mobileLink = "omni://", webAppLink = "", playStore = "", linkMode = "") -private val bitkeep: Wallet - get() = Wallet(id = "11", name = "BitKeep", homePage = "", order = "", imageUrl = "", mobileLink = "bitkeep://", webAppLink = "", playStore = "", linkMode = "") -private val tokenPocket: Wallet - get() = Wallet(id = "12", name = "TokePocket", homePage = "", order = "", imageUrl = "", mobileLink = "tokenpocket://", webAppLink = "", playStore = "", linkMode = "") -private val ledgerLive: Wallet - get() = Wallet(id = "13", name = "Ledger Live", homePage = "", order = "", imageUrl = "", mobileLink = "ledgerlive://", webAppLink = "", playStore = "", linkMode = "") -private val frontier: Wallet - get() = Wallet(id = "14", name = "Frontier", homePage = "", order = "", imageUrl = "", mobileLink = "frontier://", webAppLink = "", playStore = "", linkMode = "") -private val safePal: Wallet - get() = Wallet(id = "15", name = "SafePal", homePage = "", order = "", imageUrl = "", mobileLink = "safepal://", webAppLink = "", playStore = "", linkMode = "") - -internal val testWallets: List - get() = listOf(metaMask, trustWallet, safe, rainbow, zerion, argent, spot, imToken, alphaWallet, omni, bitkeep, tokenPocket, ledgerLive, frontier, safePal) diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/MainDispatcherRule.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/MainDispatcherRule.kt deleted file mode 100644 index fdc6e96fe..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/MainDispatcherRule.kt +++ /dev/null @@ -1,30 +0,0 @@ -@file:OptIn(ExperimentalCoroutinesApi::class) - -package com.walletconnect.wcmodal.ui - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestDispatcher -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.junit.rules.TestWatcher -import org.junit.runner.Description - -/** - * A JUnit [TestRule] that sets the Main dispatcher to [testDispatcher] - * for the duration of the test. - */ -class MainDispatcherRule( - val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() -) : TestWatcher() { - override fun starting(description: Description) { - super.starting(description) - Dispatchers.setMain(testDispatcher) - } - - override fun finished(description: Description) { - super.finished(description) - Dispatchers.resetMain() - } -} \ No newline at end of file diff --git a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/ScreenShotTest.kt b/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/ScreenShotTest.kt deleted file mode 100644 index c35de04f3..000000000 --- a/product/walletconnectmodal/src/test/kotlin/com/walletconnect/wcmodal/utils/ScreenShotTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.wcmodal.utils - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import app.cash.paparazzi.DeviceConfig -import app.cash.paparazzi.Paparazzi -import com.android.ide.common.rendering.api.SessionParams -import com.android.resources.NightMode -import com.walletconnect.wcmodal.ui.theme.ModalTheme -import com.walletconnect.wcmodal.ui.theme.ProvideModalThemeComposition -import org.junit.Rule - -abstract class ScreenShotTest { - - @get:Rule - val paparazzi = Paparazzi( - deviceConfig = DeviceConfig.PIXEL_2_XL, - renderingMode = SessionParams.RenderingMode.SHRINK, - ) - - fun runScreenShotTest( - isDarkMode: Boolean = false, - content: @Composable () -> Unit - ) { - val mode = if (isDarkMode) NightMode.NIGHT else NightMode.NOTNIGHT - paparazzi.unsafeUpdateConfig(DeviceConfig(nightMode = mode)) - paparazzi.snapshot { - ProvideModalThemeComposition { - Box(modifier = Modifier.background(ModalTheme.colors.background)) { - content() - } - } - } - } -} diff --git a/product/web3wallet/.gitignore b/product/walletkit/.gitignore similarity index 100% rename from product/web3wallet/.gitignore rename to product/walletkit/.gitignore diff --git a/product/walletkit/ReadMe.md b/product/walletkit/ReadMe.md new file mode 100644 index 000000000..e0116f800 --- /dev/null +++ b/product/walletkit/ReadMe.md @@ -0,0 +1,35 @@ +# **WalletKit - Kotlin** + +Kotlin implementation of WalletKit for Android applications. + +![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/web3wallet) + +## Requirements + +* Android min SDK 23 +* Java 11 + +## Documentation and usage +* [WalletKit installation](https://docs.walletconnect.com/2.0/kotlin/web3wallet/installation) +* [WalletKit usage](https://docs.walletconnect.com/2.0/kotlin/web3wallet/wallet-usage) + +## Installation + +root/build.gradle.kts: + +```gradle +allprojects { + repositories { + mavenCentral() + maven { url "https://jitpack.io" } + } +} +``` + +app/build.gradle.kts + +```gradle +implementation(platform("com.reown:android-bom:{BOM version}")) +implementation("com.reown:android-core") +implementation("com.reown:walletkit") +``` \ No newline at end of file diff --git a/product/walletkit/build.gradle.kts b/product/walletkit/build.gradle.kts new file mode 100644 index 000000000..3268e47b6 --- /dev/null +++ b/product/walletkit/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.library") + id(libs.plugins.kotlin.android.get().pluginId) + alias(libs.plugins.google.ksp) + id("publish-module-android") + id("jacoco-report") +} + +project.apply { + extra[KEY_PUBLISH_ARTIFACT_ID] = WALLETKIT + extra[KEY_PUBLISH_VERSION] = WALLETKIT_VERSION + extra[KEY_SDK_NAME] = "walletkit" +} + +android { + namespace = "com.reown.walletkit" + compileSdk = COMPILE_SDK + + defaultConfig { + minSdk = MIN_SDK + + aarMetadata { + minCompileSdk = MIN_SDK + } + + buildConfigField(type = "String", name = "SDK_VERSION", value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"") + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro", "${projectDir}/web3wallet-rules.pro") + } + } + lint { + abortOnError = true + ignoreWarnings = true + warningsAsErrors = false + } + + compileOptions { + sourceCompatibility = jvmVersion + targetCompatibility = jvmVersion + } + kotlinOptions { + jvmTarget = jvmVersion.toString() + freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.time.ExperimentalTime" + } + + buildFeatures { + buildConfig = true + } +} + +dependencies { + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.messaging) + + debugImplementation(project(":core:android")) + debugImplementation(project(":protocol:sign")) + + releaseImplementation("com.reown:android-core:$CORE_VERSION") + releaseImplementation("com.reown:sign:$SIGN_VERSION") +} \ No newline at end of file diff --git a/product/walletkit/src/main/AndroidManifest.xml b/product/walletkit/src/main/AndroidManifest.xml new file mode 100644 index 000000000..560dc5b67 --- /dev/null +++ b/product/walletkit/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/product/walletkit/src/main/kotlin/com/reown/walletkit/client/ClientMapper.kt b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/ClientMapper.kt new file mode 100644 index 000000000..2946b1ee2 --- /dev/null +++ b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/ClientMapper.kt @@ -0,0 +1,282 @@ +package com.reown.walletkit.client + +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.sign.client.Sign + +@JvmSynthetic +internal fun Map.toSign(): Map = + mapValues { (_, namespace) -> + Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toWallet(): Map = + mapValues { (_, namespace) -> + Wallet.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toWalletProposalNamespaces(): Map = + mapValues { (_, namespace) -> + Wallet.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toSignProposalNamespaces(): Map = + mapValues { (_, namespace) -> + Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Wallet.Model.JsonRpcResponse.toSign(): Sign.Model.JsonRpcResponse = + when (this) { + is Wallet.Model.JsonRpcResponse.JsonRpcResult -> this.toSign() + is Wallet.Model.JsonRpcResponse.JsonRpcError -> this.toSign() + } + +@JvmSynthetic +internal fun Wallet.Model.JsonRpcResponse.JsonRpcResult.toSign(): Sign.Model.JsonRpcResponse.JsonRpcResult = + Sign.Model.JsonRpcResponse.JsonRpcResult(id, result) + +@JvmSynthetic +internal fun Wallet.Model.JsonRpcResponse.JsonRpcError.toSign(): Sign.Model.JsonRpcResponse.JsonRpcError = + Sign.Model.JsonRpcResponse.JsonRpcError(id, code, message) + +@JvmSynthetic +internal fun Wallet.Model.Cacao.Signature.toSign(): Sign.Model.Cacao.Signature = Sign.Model.Cacao.Signature(t, s, m) + +@JvmSynthetic +internal fun Wallet.Model.SessionEvent.toSign(): Sign.Model.SessionEvent = Sign.Model.SessionEvent(name, data) + +@JvmSynthetic +internal fun Wallet.Model.Event.toSign(): Sign.Model.Event = Sign.Model.Event(topic, name, data, chainId) + +@JvmSynthetic +internal fun Wallet.Model.PayloadAuthRequestParams.toSign(): Sign.Model.PayloadParams = + Sign.Model.PayloadParams( + type = type ?: CacaoType.CAIP222.header, + chains = chains, + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + iat = iat + ) + +@JvmSynthetic +internal fun Sign.Model.Session.toWallet(): Wallet.Model.Session = Wallet.Model.Session( + pairingTopic, topic, expiry, requiredNamespaces.toWalletProposalNamespaces(), optionalNamespaces?.toWalletProposalNamespaces(), namespaces.toWallet(), metaData +) + +@JvmSynthetic +internal fun List.mapToPendingRequests(): List = map { request -> + Wallet.Model.PendingSessionRequest( + request.requestId, + request.topic, + request.method, + request.chainId, + request.params + ) +} + +@JvmSynthetic +internal fun List.mapToPendingSessionRequests(): List = map { request -> + Wallet.Model.SessionRequest( + request.topic, + request.chainId, + request.peerMetaData, + Wallet.Model.SessionRequest.JSONRPCRequest(request.request.id, request.request.method, request.request.params) + ) +} + +@JvmSynthetic +internal fun Sign.Model.SessionProposal.toWallet(): Wallet.Model.SessionProposal = + Wallet.Model.SessionProposal( + pairingTopic, + name, + description, + url, + icons, + redirect, + requiredNamespaces.toWalletProposalNamespaces(), + optionalNamespaces.toWalletProposalNamespaces(), + properties, + proposerPublicKey, + relayProtocol, + relayData + ) + +@JvmSynthetic +internal fun Sign.Model.SessionAuthenticate.toWallet(): Wallet.Model.SessionAuthenticate = + Wallet.Model.SessionAuthenticate(id, topic, participant.toWallet(), payloadParams.toWallet()) + +@JvmSynthetic +internal fun Sign.Model.SessionAuthenticate.Participant.toWallet(): Wallet.Model.SessionAuthenticate.Participant = Wallet.Model.SessionAuthenticate.Participant(publicKey, metadata) + +@JvmSynthetic +internal fun Sign.Model.PayloadParams.toWallet(): Wallet.Model.PayloadAuthRequestParams = + Wallet.Model.PayloadAuthRequestParams( + chains = chains, + type = type ?: CacaoType.CAIP222.header, + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + iat = iat + ) + +internal fun Sign.Model.VerifyContext.toWallet(): Wallet.Model.VerifyContext = + Wallet.Model.VerifyContext(id, origin, this.validation.toWallet(), verifyUrl, isScam) + +internal fun Sign.Model.Validation.toWallet(): Wallet.Model.Validation = + when (this) { + Sign.Model.Validation.VALID -> Wallet.Model.Validation.VALID + Sign.Model.Validation.INVALID -> Wallet.Model.Validation.INVALID + Sign.Model.Validation.UNKNOWN -> Wallet.Model.Validation.UNKNOWN + } + +@JvmSynthetic +internal fun Sign.Model.SessionRequest.toWallet(): Wallet.Model.SessionRequest = + Wallet.Model.SessionRequest( + topic = topic, + chainId = chainId, + peerMetaData = peerMetaData, + request = Wallet.Model.SessionRequest.JSONRPCRequest( + id = request.id, + method = request.method, + params = request.params + ) + ) + +@JvmSynthetic +internal fun Sign.Model.DeletedSession.toWallet(): Wallet.Model.SessionDelete = + when (this) { + is Sign.Model.DeletedSession.Success -> Wallet.Model.SessionDelete.Success(topic, reason) + is Sign.Model.DeletedSession.Error -> Wallet.Model.SessionDelete.Error(error) + } + +@JvmSynthetic +internal fun Sign.Model.SettledSessionResponse.toWallet(): Wallet.Model.SettledSessionResponse = + when (this) { + is Sign.Model.SettledSessionResponse.Result -> Wallet.Model.SettledSessionResponse.Result(session.toWallet()) + is Sign.Model.SettledSessionResponse.Error -> Wallet.Model.SettledSessionResponse.Error(errorMessage) + } + +@JvmSynthetic +internal fun Sign.Model.SessionUpdateResponse.toWallet(): Wallet.Model.SessionUpdateResponse = + when (this) { + is Sign.Model.SessionUpdateResponse.Result -> Wallet.Model.SessionUpdateResponse.Result(topic, namespaces.toWallet()) + is Sign.Model.SessionUpdateResponse.Error -> Wallet.Model.SessionUpdateResponse.Error(errorMessage) + } + +@JvmSynthetic +internal fun Sign.Model.ExpiredProposal.toWallet(): Wallet.Model.ExpiredProposal = Wallet.Model.ExpiredProposal(pairingTopic, proposerPublicKey) + +@JvmSynthetic +internal fun Sign.Model.ExpiredRequest.toWallet(): Wallet.Model.ExpiredRequest = Wallet.Model.ExpiredRequest(topic, id) + +@JvmSynthetic +internal fun Wallet.Model.SessionProposal.toSign(): Sign.Model.SessionProposal = + Sign.Model.SessionProposal( + pairingTopic, + name, + description, + url, + icons, + redirect, + requiredNamespaces.toSignProposalNamespaces(), + optionalNamespaces.toSignProposalNamespaces(), + properties, + proposerPublicKey, + relayProtocol, + relayData + ) + +@JvmSynthetic +internal fun Sign.Model.Message.SessionProposal.toWallet(): Wallet.Model.Message.SessionProposal = + Wallet.Model.Message.SessionProposal( + id, + pairingTopic, + name, + description, + url, + icons, + redirect, + requiredNamespaces.toWalletProposalNamespaces(), + optionalNamespaces.toWalletProposalNamespaces(), + properties, + proposerPublicKey, + relayProtocol, + relayData + ) + +@JvmSynthetic +internal fun Sign.Model.Message.SessionRequest.toWallet(): Wallet.Model.Message.SessionRequest = + Wallet.Model.Message.SessionRequest( + topic, + chainId, + peerMetaData, + Wallet.Model.Message.SessionRequest.JSONRPCRequest(request.id, request.method, request.params) + ) + +@JvmSynthetic +internal fun List.toSign(): List = mutableListOf().apply { + this@toSign.forEach { cacao: Wallet.Model.Cacao -> + with(cacao) { + add( + Sign.Model.Cacao( + Sign.Model.Cacao.Header(header.t), + Sign.Model.Cacao.Payload( + payload.iss, + payload.domain, + payload.aud, + payload.version, + payload.nonce, + payload.iat, + payload.nbf, + payload.exp, + payload.statement, + payload.requestId, + payload.resources + ), + Sign.Model.Cacao.Signature(signature.t, signature.s, signature.m) + ) + ) + } + } +} + +@JvmSynthetic +internal fun Sign.Model.Cacao.toWallet(): Wallet.Model.Cacao = with(this) { + Wallet.Model.Cacao( + Wallet.Model.Cacao.Header(header.t), + Wallet.Model.Cacao.Payload( + payload.iss, + payload.domain, + payload.aud, + payload.version, + payload.nonce, + payload.iat, + payload.nbf, + payload.exp, + payload.statement, + payload.requestId, + payload.resources + ), + Wallet.Model.Cacao.Signature(signature.t, signature.s, signature.m) + ) +} + +@JvmSynthetic +internal fun Sign.Model.ConnectionState.Reason.toWallet(): Wallet.Model.ConnectionState.Reason = when (this) { + is Sign.Model.ConnectionState.Reason.ConnectionClosed -> Wallet.Model.ConnectionState.Reason.ConnectionClosed(this.message) + is Sign.Model.ConnectionState.Reason.ConnectionFailed -> Wallet.Model.ConnectionState.Reason.ConnectionFailed(this.throwable) +} \ No newline at end of file diff --git a/product/walletkit/src/main/kotlin/com/reown/walletkit/client/Wallet.kt b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/Wallet.kt new file mode 100644 index 000000000..bf6ad5217 --- /dev/null +++ b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/Wallet.kt @@ -0,0 +1,312 @@ +package com.reown.walletkit.client + +import androidx.annotation.Keep +import com.reown.android.Core +import com.reown.android.CoreInterface +import com.reown.android.cacao.SignatureInterface +import java.net.URI +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +object Wallet { + + sealed interface Listeners { + interface SessionPing : Listeners { + fun onSuccess(pingSuccess: Model.Ping.Success) + fun onError(pingError: Model.Ping.Error) + } + } + + sealed class Params { + data class Init(val core: CoreInterface) : Params() + + data class Pair(val uri: String) : Params() + + data class SessionApprove( + val proposerPublicKey: String, + val namespaces: Map, + val relayProtocol: String? = null, + ) : Params() + + data class ApproveSessionAuthenticate(val id: Long, val auths: List) : Params() + + data class RejectSessionAuthenticate(val id: Long, val reason: String) : Params() + + data class SessionReject(val proposerPublicKey: String, val reason: String) : Params() + + data class SessionUpdate(val sessionTopic: String, val namespaces: Map) : Params() + + data class SessionExtend(val topic: String) : Params() + + data class SessionEmit(val topic: String, val event: Model.SessionEvent, val chainId: String) : Params() + + data class SessionRequestResponse(val sessionTopic: String, val jsonRpcResponse: Model.JsonRpcResponse) : Params() + + data class SessionDisconnect(val sessionTopic: String) : Params() + + data class FormatMessage(val payloadParams: Model.PayloadParams, val issuer: String) : Params() + + data class FormatAuthMessage(val payloadParams: Model.PayloadAuthRequestParams, val issuer: String) : Params() + + data class Ping(val sessionTopic: String, val timeout: Duration = 30.seconds) : Params() + + sealed class AuthRequestResponse : Params() { + abstract val id: Long + + data class Result(override val id: Long, val signature: Model.Cacao.Signature, val issuer: String) : AuthRequestResponse() + data class Error(override val id: Long, val code: Int, val message: String) : AuthRequestResponse() + } + + data class DecryptMessage(val topic: String, val encryptedMessage: String) : Params() + } + + sealed class Model { + + sealed class Ping : Model() { + data class Success(val topic: String) : Ping() + data class Error(val error: Throwable) : Ping() + } + + data class Error(val throwable: Throwable) : Model() + + data class ConnectionState(val isAvailable: Boolean, val reason: Reason? = null) : Model() { + sealed class Reason : Model() { + data class ConnectionClosed(val message: String) : Reason() + data class ConnectionFailed(val throwable: Throwable) : Reason() + } + } + + data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : Model() + data class ExpiredRequest(val topic: String, val id: Long) : Model() + + data class SessionProposal( + val pairingTopic: String, + val name: String, + val description: String, + val url: String, + val icons: List, + val redirect: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map, + val properties: Map?, + val proposerPublicKey: String, + val relayProtocol: String, + val relayData: String?, + ) : Model() + + data class SessionAuthenticate( + val id: Long, + val pairingTopic: String, + val participant: Participant, + val payloadParams: PayloadAuthRequestParams, + ) : Model() { + data class Participant( + val publicKey: String, + val metadata: Core.Model.AppMetaData?, + ) : Model() + } + + data class VerifyContext( + val id: Long, + val origin: String, + val validation: Validation, + val verifyUrl: String, + val isScam: Boolean?, + ) : Model() + + enum class Validation { + VALID, INVALID, UNKNOWN + } + + data class SessionRequest( + val topic: String, + val chainId: String?, + val peerMetaData: Core.Model.AppMetaData?, + val request: JSONRPCRequest, + ) : Model() { + + data class JSONRPCRequest( + val id: Long, + val method: String, + val params: String, + ) : Model() + } + + sealed class SettledSessionResponse : Model() { + data class Result(val session: Session) : SettledSessionResponse() + data class Error(val errorMessage: String) : SettledSessionResponse() + } + + sealed class SessionUpdateResponse : Model() { + data class Result(val topic: String, val namespaces: Map) : SessionUpdateResponse() + data class Error(val errorMessage: String) : SessionUpdateResponse() + } + + sealed class SessionDelete : Model() { + data class Success(val topic: String, val reason: String) : SessionDelete() + data class Error(val error: Throwable) : SessionDelete() + } + + sealed class Namespace : Model() { + + //Required or Optional + data class Proposal( + val chains: List? = null, + val methods: List, + val events: List, + ) : Namespace() + + data class Session( + val chains: List? = null, + val accounts: List, + val methods: List, + val events: List, + ) : Namespace() + } + + sealed class JsonRpcResponse : Model() { + abstract val id: Long + val jsonrpc: String = "2.0" + + data class JsonRpcResult( + override val id: Long, + val result: String, + ) : JsonRpcResponse() + + data class JsonRpcError( + override val id: Long, + val code: Int, + val message: String, + ) : JsonRpcResponse() + } + + data class PayloadParams( + val type: String, + val chainId: String, + val domain: String, + val aud: String, + val version: String, + val nonce: String, + val iat: String, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + val resources: List?, + ) : Model() + + data class PayloadAuthRequestParams( + val chains: List, + val domain: String, + val nonce: String, + val aud: String, + val type: String?, + val iat: String, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + val resources: List? + ) : Model() + + data class SessionEvent( + val name: String, + val data: String, + ) : Model() + + data class Event( + val topic: String, + val name: String, + val data: String, + val chainId: String, + ) : Model() + + data class Cacao( + val header: Header, + val payload: Payload, + val signature: Signature, + ) : Model() { + @Keep + data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface + data class Header(val t: String) : Model() + data class Payload( + val iss: String, + val domain: String, + val aud: String, + val version: String, + val nonce: String, + val iat: String, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + val resources: List?, + ) : Model() { + val address: String get() = iss.split(ISS_DELIMITER)[ISS_POSITION_OF_ADDRESS] + + private companion object { + const val ISS_DELIMITER = ":" + const val ISS_POSITION_OF_ADDRESS = 4 + } + } + } + + data class Session( + @Deprecated("Pairing topic is deprecated") + val pairingTopic: String, + val topic: String, + val expiry: Long, + val requiredNamespaces: Map, + val optionalNamespaces: Map?, + val namespaces: Map, + val metaData: Core.Model.AppMetaData?, + ) : Model() { + val redirect: String? = metaData?.redirect + } + + data class PendingSessionRequest( + val requestId: Long, + val topic: String, + val method: String, + val chainId: String?, + val params: String, + ) : Model() + + sealed class Message : Model() { + + data class Simple( + val title: String, + val body: String, + ) : Message() + + data class SessionProposal( + val id: Long, + val pairingTopic: String, + val name: String, + val description: String, + val url: String, + val icons: List, + val redirect: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map, + val properties: Map?, + val proposerPublicKey: String, + val relayProtocol: String, + val relayData: String?, + ) : Message() + + data class SessionRequest( + val topic: String, + val chainId: String?, + val peerMetaData: Core.Model.AppMetaData?, + val request: JSONRPCRequest, + ) : Message() { + data class JSONRPCRequest( + val id: Long, + val method: String, + val params: String, + ) : Message() + } + } + } +} \ No newline at end of file diff --git a/product/walletkit/src/main/kotlin/com/reown/walletkit/client/WalletKit.kt b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/WalletKit.kt new file mode 100644 index 000000000..d881f99b8 --- /dev/null +++ b/product/walletkit/src/main/kotlin/com/reown/walletkit/client/WalletKit.kt @@ -0,0 +1,363 @@ +package com.reown.walletkit.client + +import com.reown.android.Core +import com.reown.android.CoreInterface +import com.reown.android.internal.common.scope +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.common.exceptions.SignClientAlreadyInitializedException +import kotlinx.coroutines.* +import java.util.* + +object WalletKit { + private lateinit var coreClient: CoreInterface + + interface WalletDelegate { + fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal, verifyContext: Wallet.Model.VerifyContext) + val onSessionAuthenticate: ((Wallet.Model.SessionAuthenticate, Wallet.Model.VerifyContext) -> Unit)? get() = null + fun onSessionRequest(sessionRequest: Wallet.Model.SessionRequest, verifyContext: Wallet.Model.VerifyContext) + fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) + fun onSessionExtend(session: Wallet.Model.Session) + + //Responses + fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) + fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) + + //Utils + fun onProposalExpired(proposal: Wallet.Model.ExpiredProposal) { + //override me + } + + fun onRequestExpired(request: Wallet.Model.ExpiredRequest) { + //override me + } + + fun onConnectionStateChange(state: Wallet.Model.ConnectionState) + fun onError(error: Wallet.Model.Error) + } + + @Throws(IllegalStateException::class) + fun setWalletDelegate(delegate: WalletDelegate) { + val isSessionAuthenticateImplemented = delegate.onSessionAuthenticate != null + + val signWalletDelegate = object : SignClient.WalletDelegate { + override fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal, verifyContext: Sign.Model.VerifyContext) { + delegate.onSessionProposal(sessionProposal.toWallet(), verifyContext.toWallet()) + } + + override val onSessionAuthenticate: ((Sign.Model.SessionAuthenticate, Sign.Model.VerifyContext) -> Unit)? + get() = if (isSessionAuthenticateImplemented) { + { sessionAuthenticate, verifyContext -> + delegate.onSessionAuthenticate?.invoke(sessionAuthenticate.toWallet(), verifyContext.toWallet()) + } + } else { + null + } + + override fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest, verifyContext: Sign.Model.VerifyContext) { + delegate.onSessionRequest(sessionRequest.toWallet(), verifyContext.toWallet()) + } + + override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { + delegate.onSessionDelete(deletedSession.toWallet()) + } + + override fun onSessionExtend(session: Sign.Model.Session) { + delegate.onSessionExtend(session.toWallet()) + } + + override fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) { + delegate.onSessionSettleResponse(settleSessionResponse.toWallet()) + } + + override fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) { + delegate.onSessionUpdateResponse(sessionUpdateResponse.toWallet()) + } + + override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { + delegate.onProposalExpired(proposal.toWallet()) + } + + override fun onRequestExpired(request: Sign.Model.ExpiredRequest) { + delegate.onRequestExpired(request.toWallet()) + } + + override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { + delegate.onConnectionStateChange(Wallet.Model.ConnectionState(state.isAvailable, state.reason?.toWallet())) + } + + override fun onError(error: Sign.Model.Error) { + delegate.onError(Wallet.Model.Error(error.throwable)) + } + } + + SignClient.setWalletDelegate(signWalletDelegate) + } + + @Throws(IllegalStateException::class) + fun initialize(params: Wallet.Params.Init, onSuccess: () -> Unit = {}, onError: (Wallet.Model.Error) -> Unit) { + coreClient = params.core + var clientInitCounter = 0 + val onSuccessfulInitialization: () -> Unit = { clientInitCounter++ } + + SignClient.initialize(Sign.Params.Init(params.core), onSuccess = onSuccessfulInitialization) { error -> + if (error.throwable is SignClientAlreadyInitializedException) { + onSuccessfulInitialization() + } else { + onError(Wallet.Model.Error(error.throwable)) + } + } + validateInitializationCount(clientInitCounter, onSuccess, onError) + } + + @Throws(IllegalStateException::class) + fun registerDeviceToken(firebaseAccessToken: String, enableEncrypted: Boolean = false, onSuccess: () -> Unit, onError: (Wallet.Model.Error) -> Unit) { + coreClient.Echo.register(firebaseAccessToken, enableEncrypted, onSuccess) { error -> onError(Wallet.Model.Error(error)) } + } + + @Throws(IllegalStateException::class) + fun decryptMessage(params: Wallet.Params.DecryptMessage, onSuccess: (Wallet.Model.Message) -> Unit, onError: (Wallet.Model.Error) -> Unit) { + scope.launch { + SignClient.decryptMessage( + Sign.Params.DecryptMessage(params.topic, params.encryptedMessage), + onSuccess = { message -> + when (message) { + is Sign.Model.Message.SessionRequest -> onSuccess(message.toWallet()) + is Sign.Model.Message.SessionProposal -> onSuccess(message.toWallet()) + else -> { /*Ignore*/ + } + } + }, + onError = { signError -> onError(Wallet.Model.Error(signError.throwable)) }) + } + } + + @Throws(IllegalStateException::class) + fun dispatchEnvelope(urlWithEnvelope: String, onError: (Wallet.Model.Error) -> Unit) { + scope.launch { + try { + SignClient.dispatchEnvelope(urlWithEnvelope) { error -> onError(Wallet.Model.Error(error.throwable)) } + } catch (error: Exception) { + onError(Wallet.Model.Error(error)) + } + } + } + + @Throws(IllegalStateException::class) + fun pair(params: Wallet.Params.Pair, onSuccess: (Wallet.Params.Pair) -> Unit = {}, onError: (Wallet.Model.Error) -> Unit = {}) { + coreClient.Pairing.pair(Core.Params.Pair(params.uri), { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun approveSession( + params: Wallet.Params.SessionApprove, + onSuccess: (Wallet.Params.SessionApprove) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Approve(params.proposerPublicKey, params.namespaces.toSign(), params.relayProtocol) + SignClient.approveSession(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(Exception::class) + fun generateApprovedNamespaces(sessionProposal: Wallet.Model.SessionProposal, supportedNamespaces: Map): Map { + return com.reown.sign.client.utils.generateApprovedNamespaces(sessionProposal.toSign(), supportedNamespaces.toSign()).toWallet() + } + + @Throws(IllegalStateException::class) + fun rejectSession( + params: Wallet.Params.SessionReject, + onSuccess: (Wallet.Params.SessionReject) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Reject(params.proposerPublicKey, params.reason) + SignClient.rejectSession(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun approveSessionAuthenticate( + params: Wallet.Params.ApproveSessionAuthenticate, + onSuccess: (Wallet.Params.ApproveSessionAuthenticate) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.ApproveAuthenticate(params.id, params.auths.toSign()) + SignClient.approveAuthenticate(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun rejectSessionAuthenticate( + params: Wallet.Params.RejectSessionAuthenticate, + onSuccess: (Wallet.Params.RejectSessionAuthenticate) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.RejectAuthenticate(params.id, params.reason) + SignClient.rejectAuthenticate(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(Exception::class) + fun generateAuthObject(payloadParams: Wallet.Model.PayloadAuthRequestParams, issuer: String, signature: Wallet.Model.Cacao.Signature): Wallet.Model.Cacao { + return com.reown.sign.client.utils.generateAuthObject(payloadParams.toSign(), issuer, signature.toSign()).toWallet() + } + + @Throws(Exception::class) + fun generateAuthPayloadParams(payloadParams: Wallet.Model.PayloadAuthRequestParams, supportedChains: List, supportedMethods: List): Wallet.Model.PayloadAuthRequestParams { + return com.reown.sign.client.utils.generateAuthPayloadParams(payloadParams.toSign(), supportedChains, supportedMethods).toWallet() + } + + @Throws(IllegalStateException::class) + fun updateSession( + params: Wallet.Params.SessionUpdate, + onSuccess: (Wallet.Params.SessionUpdate) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Update(params.sessionTopic, params.namespaces.toSign()) + SignClient.update(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun extendSession( + params: Wallet.Params.SessionExtend, + onSuccess: (Wallet.Params.SessionExtend) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Extend(params.topic) + SignClient.extend(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun respondSessionRequest( + params: Wallet.Params.SessionRequestResponse, + onSuccess: (Wallet.Params.SessionRequestResponse) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Response(params.sessionTopic, params.jsonRpcResponse.toSign()) + SignClient.respond(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + + @Throws(IllegalStateException::class) + fun emitSessionEvent( + params: Wallet.Params.SessionEmit, + onSuccess: (Wallet.Params.SessionEmit) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Emit(params.topic, params.event.toSign(), params.chainId) + SignClient.emit(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun disconnectSession( + params: Wallet.Params.SessionDisconnect, + onSuccess: (Wallet.Params.SessionDisconnect) -> Unit = {}, + onError: (Wallet.Model.Error) -> Unit, + ) { + val signParams = Sign.Params.Disconnect(params.sessionTopic) + SignClient.disconnect(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) + } + + @Throws(IllegalStateException::class) + fun pingSession( + params: Wallet.Params.Ping, + sessionPing: Wallet.Listeners.SessionPing? + ) { + val signParams = Sign.Params.Ping(params.sessionTopic) + val signPingLister = object : Sign.Listeners.SessionPing { + override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { + sessionPing?.onSuccess(Wallet.Model.Ping.Success(pingSuccess.topic)) + } + + override fun onError(pingError: Sign.Model.Ping.Error) { + sessionPing?.onError(Wallet.Model.Ping.Error(pingError.error)) + } + } + + SignClient.ping(signParams, signPingLister) + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + fun formatAuthMessage(params: Wallet.Params.FormatAuthMessage): String { + val signParams = Sign.Params.FormatMessage(params.payloadParams.toSign(), params.issuer) + return SignClient.formatAuthMessage(signParams) + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + @JvmStatic + fun getListOfActiveSessions(): List { + return SignClient.getListOfActiveSessions().map(Sign.Model.Session::toWallet) + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + fun getActiveSessionByTopic(topic: String): Wallet.Model.Session? { + return SignClient.getActiveSessionByTopic(topic)?.toWallet() + } + + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + + @Throws(IllegalStateException::class) + fun getPendingListOfSessionRequests(topic: String): List { + return SignClient.getPendingSessionRequests(topic).mapToPendingSessionRequests() + } + + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + fun getSessionProposals(): List { + return SignClient.getSessionProposals().map(Sign.Model.SessionProposal::toWallet) + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + fun getVerifyContext(id: Long): Wallet.Model.VerifyContext? { + return SignClient.getVerifyContext(id)?.toWallet() + } + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + @Throws(IllegalStateException::class) + fun getListOfVerifyContexts(): List { + return SignClient.getListOfVerifyContexts().map { verifyContext -> verifyContext.toWallet() } + } + + private fun validateInitializationCount(clientInitCounter: Int, onSuccess: () -> Unit, onError: (Wallet.Model.Error) -> Unit) { + scope.launch { + try { + withTimeout(TIMEOUT) { + while (true) { + if (clientInitCounter == 2) { + onSuccess() + return@withTimeout + } + delay(100) + } + } + } catch (e: Exception) { + onError(Wallet.Model.Error(e)) + } + } + } + + private const val TIMEOUT: Long = 10000 +} diff --git a/product/walletkit/src/main/kotlin/com/reown/walletkit/utils/CacaoSigner.kt b/product/walletkit/src/main/kotlin/com/reown/walletkit/utils/CacaoSigner.kt new file mode 100644 index 000000000..4551ae884 --- /dev/null +++ b/product/walletkit/src/main/kotlin/com/reown/walletkit/utils/CacaoSigner.kt @@ -0,0 +1,16 @@ +@file:JvmSynthetic + +package com.reown.walletkit.utils + +import com.reown.android.cacao.signature.ISignatureType +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.walletkit.client.Wallet + + +/// Only added to have backwards compatibility. Newer SDKs should only add CacaoSigner object below. +@Deprecated("Moved to android-core module, as other SDKs also need CACAO.", ReplaceWith("com.reown.android.internal.common.cacao.signature.SignatureType")) +enum class SignatureType(override val header: String) : ISignatureType { + EIP191("eip191"), EIP1271("eip1271"); +} + +object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/product/walletkit/web3wallet-rules.pro b/product/walletkit/web3wallet-rules.pro new file mode 100644 index 000000000..d7bb92f85 --- /dev/null +++ b/product/walletkit/web3wallet-rules.pro @@ -0,0 +1,4 @@ +-keep class com.reown.walletkit.client.Wallet$Model$Cacao$Signature { *; } +-keep class com.reown.walletkit.client.Wallet$Model$Cacao { *; } +-keep class com.reown.walletkit.client.Wallet$Model { *; } +-keep class com.reown.walletkit.client.Wallet { *; } \ No newline at end of file diff --git a/product/web3modal/ReadMe.md b/product/web3modal/ReadMe.md deleted file mode 100644 index 99476fb60..000000000 --- a/product/web3modal/ReadMe.md +++ /dev/null @@ -1,40 +0,0 @@ -# **WalletConnect Web3Modal - Kotlin** - -# Installation - -:::caution -**The Web3Modal SDK is currently in Alpha and is not production-ready**. - -It's public API and associated documentation may still see significant and breaking changes. -::: - -Kotlin implementation of Web3Modal for Android applications. - -Android Core ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/android-core) - -Web3Modal ![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/web3modal) - -## Requirements - -* Android min SDK 23 -* Java 11 - -## Installation -root/build.gradle.kts: -```gradle -allprojects { - repositories { - mavenCentral() - maven { url "https://jitpack.io" } - } -} -``` - -app/build.gradle.kts - -```gradle -implementation(platform("com.walletconnect:android-bom:$BOM_VERSION")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:web3modal") -``` - diff --git a/product/web3modal/build.gradle.kts b/product/web3modal/build.gradle.kts deleted file mode 100644 index 3048423f0..000000000 --- a/product/web3modal/build.gradle.kts +++ /dev/null @@ -1,107 +0,0 @@ -plugins { - id("com.android.library") - id(libs.plugins.kotlin.android.get().pluginId) - alias(libs.plugins.google.ksp) - alias(libs.plugins.paparazzi) - id("publish-module-android") - id("jacoco-report") -} - -project.apply { - extra[KEY_PUBLISH_ARTIFACT_ID] = WEB_3_MODAL - extra[KEY_PUBLISH_VERSION] = WEB_3_MODAL_VERSION - extra[KEY_SDK_NAME] = "web3modal" -} - -android { - namespace = "com.walletconnect.web3.modal" - compileSdk = COMPILE_SDK - - defaultConfig { - minSdk = MIN_SDK - - aarMetadata { - minCompileSdk = MIN_SDK - } - - buildConfigField(type = "String", name = "SDK_VERSION", value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"") - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - - File("${rootDir.path}/gradle/consumer-rules").listFiles()?.let { proguardFiles -> - consumerProguardFiles(*proguardFiles) - } - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro") - } - } - lint { - abortOnError = true - ignoreWarnings = true - warningsAsErrors = false - } - - compileOptions { - sourceCompatibility = jvmVersion - targetCompatibility = jvmVersion - } - kotlinOptions { - jvmTarget = jvmVersion.toString() - freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.time.ExperimentalTime" - } - buildFeatures { - compose = true - buildConfig = true - } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() - } - - tasks.withType(Test::class.java) { - jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods") - } -} - -dependencies { - - implementation(libs.bundles.androidxAppCompat) - implementation(libs.bundles.accompanist) - implementation(libs.coil) - - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.compose.ui) - implementation(libs.androidx.compose.ui.tooling.preview) - implementation(libs.androidx.compose.material) - implementation(libs.androidx.compose.navigation) - implementation(libs.androidx.compose.lifecycle) - debugImplementation(libs.androidx.compose.ui.tooling) - debugImplementation(libs.androidx.compose.ui.test.manifest) - androidTestImplementation(libs.androidx.compose.ui.test.junit) - androidTestImplementation(libs.androidx.compose.navigation.testing) - - implementation(libs.androidx.datastore) - implementation(libs.bundles.androidxLifecycle) - ksp(libs.moshi.ksp) - api(libs.bundles.androidxNavigation) - implementation(libs.qrCodeGenerator) - implementation(libs.coinbaseWallet) - - testImplementation(libs.coroutines.test) - testImplementation(libs.turbine) - - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") - releaseImplementation("com.walletconnect:sign:$SIGN_VERSION") - releaseImplementation("com.walletconnect:modal-core:$MODAL_CORE_VERSION") - - debugImplementation(project(":core:android")) - debugImplementation(project(":protocol:sign")) - debugImplementation(project(":core:modal")) - - testImplementation(libs.bundles.androidxTest) - - androidTestUtil(libs.androidx.testOrchestrator) - androidTestImplementation(libs.bundles.androidxAndroidTest) -} \ No newline at end of file diff --git a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBarTest.kt b/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBarTest.kt deleted file mode 100644 index 2eb214010..000000000 --- a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBarTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal - -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithContentDescription -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import com.walletconnect.web3.modal.ui.components.internal.commons.BackArrowIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.components.internal.commons.TestTags -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import org.junit.Rule -import org.junit.Test -import org.junit.Assert.* - -class Web3ModalTopBarTest { - - @get:Rule - val composeTestRule = createComposeRule() - - @Test - fun web3ModalTopBar_titleShouldBeShown() { - composeTestRule.setContent { - ProvideWeb3ModalThemeComposition { - Web3ModalTopBar(title = "Title", startIcon = { BackArrowIcon { } }, onCloseIconClick = {}) - } - } - - composeTestRule.onNodeWithTag(TestTags.TITLE).assertExists() - composeTestRule.onNodeWithText("Title").assertExists() - } - - @Test - fun web3ModalTopBar_onCloseCallbackIsTriggered() { - var isClicked = false - - composeTestRule.setContent { - ProvideWeb3ModalThemeComposition { - Web3ModalTopBar(title = "Title", startIcon = { BackArrowIcon { } }, onCloseIconClick = { isClicked = true}) - } - - } - composeTestRule.onNodeWithContentDescription(ContentDescription.CLOSE.description).performClick() - - assertEquals(isClicked, true) - } -} \ No newline at end of file diff --git a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootStateTest.kt b/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootStateTest.kt deleted file mode 100644 index 858f618be..000000000 --- a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootStateTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.root - -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.remember -import androidx.compose.ui.test.junit4.createComposeRule -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.rememberTestNavController -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Rule -import org.junit.Test - -class Web3ModalRootStateTest { - - @get:Rule - val composeTestRule = createComposeRule() - - private lateinit var state: Web3ModalRootState - - @Test - fun web3modalRootState_currentDestination() = runTest { - var currentDestinationPath: String? = null - - composeTestRule.setContent { - val navController = rememberTestNavController() - state = remember(navController) { - Web3ModalRootState( - coroutineScope = backgroundScope, - navController = navController - ) - } - - currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route - - LaunchedEffect(Unit) { - navController.setCurrentDestination(Route.QR_CODE.path) - } - } - - assertEquals(Route.QR_CODE.path, currentDestinationPath) - } - - @Test - fun web3ModalRootState_canPopup_fromInitDestination() = runTest { - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - } - - assertEquals(false, state.canPopUp) - } - - @Test - fun web3ModalRootState_canPopup_fromQRCode() = runTest { - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - - LaunchedEffect(Unit) { - navController.setCurrentDestination(Route.QR_CODE.path) - } - } - - assertEquals(true, state.canPopUp) - } - - @Test - fun web3ModalRouteState_popUp_shouldNavigateBack() = runTest { - var currentDestinationPath: String? = null - - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - - currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route - - LaunchedEffect(Unit) { - navController.navigate(Route.QR_CODE.path) - state.popUp() - } - } - - assertEquals(Route.CONNECT_YOUR_WALLET.path, currentDestinationPath) - } - - @Test - fun web3ModalRouteState_title_shouldBeTakenFromEnum() = runTest { - var title: String? = null - - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - - title = state.title.collectAsState(initial = null).value - } - - assertEquals(Route.CONNECT_YOUR_WALLET.title, title) - } - - @Test - fun web3ModalRouteState_title_shouldBeTakenFromArg() = runTest { - var title: String? = null - - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - - title = state.title.collectAsState(initial = null).value - - LaunchedEffect(Unit) { - navController.navigate(Route.REDIRECT.path + "&" + "title") - } - } - - assertEquals("title", title) - } - - @Test - fun web3ModalRouteState_navigateToHelp() = runTest { - var currentDestinationPath: String? = null - - composeTestRule.setContent { - val navController = rememberTestNavController() - state = rememberWeb3ModalRootState(coroutineScope = backgroundScope, navController = navController) - - currentDestinationPath = state.currentDestinationFlow.collectAsState(null).value?.destination?.route - - LaunchedEffect(Unit) { - state.navigateToHelp() - } - } - - assertEquals(Route.WHAT_IS_WALLET.path, currentDestinationPath) - } -} diff --git a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootTest.kt b/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootTest.kt deleted file mode 100644 index 5186bebd9..000000000 --- a/product/web3modal/src/androidTest/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootTest.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.root - -import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithContentDescription -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.components.internal.commons.TestTags -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.rememberTestNavController -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import org.junit.Rule -import org.junit.Test - -class Web3ModalRootTest { - - @get:Rule - val composeTestRule = createComposeRule() - - @Test - fun web3ModalRoot_shouldShowTitleFromEnumRoute() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - } - - composeTestRule.onNodeWithTag(TestTags.TITLE).assertExists() - composeTestRule.onNodeWithText(Route.CONNECT_YOUR_WALLET.title!!).assertExists() - } - - @Test - fun web3ModalRoot_shouldShowTitleFromNavArg() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - - LaunchedEffect(Unit) { - controller.navigate(Route.REDIRECT.path + "&" + "title") - } - } - - composeTestRule.onNodeWithTag("Title").assertExists() - composeTestRule.onNodeWithText("title").assertExists() - } - - @Test - fun web3ModalRoot_shouldShowWithoutTitle() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - - LaunchedEffect(Unit) { - controller.navigate("A") - } - } - - composeTestRule.onNodeWithTag("Title").assertDoesNotExist() - } - - @Test - fun web3ModalRoot_QuestionMarkIcon() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - } - - composeTestRule.onNodeWithContentDescription(ContentDescription.QUESTION_MARK.description) - } - - @Test - fun web3ModalRoot_BackIcon() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - } - - composeTestRule.onNodeWithContentDescription(ContentDescription.BACK_ARROW.description) - } - - @Test - fun web3ModalRoot_closeIsCalled() { - composeTestRule.setContent { - val controller = rememberTestNavController() - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(navController = controller, closeModal = { }) { - Box {} - } - } - } - - composeTestRule.onNodeWithContentDescription(ContentDescription.BACK_ARROW.description) - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/ClientMapper.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/ClientMapper.kt deleted file mode 100644 index 0826a2119..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/ClientMapper.kt +++ /dev/null @@ -1,164 +0,0 @@ -package com.walletconnect.web3.modal.client - -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.sign.client.Sign -import com.walletconnect.web3.modal.client.models.Account -import com.walletconnect.web3.modal.client.models.Session -import com.walletconnect.web3.modal.client.models.request.Request -import java.text.SimpleDateFormat -import java.util.Calendar - -internal fun Sign.Model.ApprovedSession.toModal() = Modal.Model.ApprovedSession.WalletConnectSession(topic, metaData, namespaces.toModal(), accounts) - -internal fun Map.toModal() = - mapValues { (_, namespace) -> Modal.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) } - -internal fun Sign.Model.RejectedSession.toModal() = Modal.Model.RejectedSession(topic, reason) - -internal fun Sign.Model.UpdatedSession.toModal() = Modal.Model.UpdatedSession(topic, namespaces.toModal()) - -internal fun Sign.Model.SessionEvent.toModal() = Modal.Model.SessionEvent(name, data) - -internal fun Sign.Model.Event.toModal() = Modal.Model.Event(topic, name, data, chainId) - -internal fun Sign.Model.Session.toModal() = Modal.Model.Session(pairingTopic, topic, expiry, namespaces.toModal(), metaData) - -internal fun Sign.Model.DeletedSession.toModal() = when (this) { - is Sign.Model.DeletedSession.Error -> Modal.Model.DeletedSession.Error(error) - is Sign.Model.DeletedSession.Success -> Modal.Model.DeletedSession.Success(topic, reason) -} - -internal fun Sign.Model.SessionRequestResponse.toModal() = Modal.Model.SessionRequestResponse(topic, chainId, method, result.toModal()) - -internal fun Sign.Model.JsonRpcResponse.toModal() = when (this) { - is Sign.Model.JsonRpcResponse.JsonRpcError -> Modal.Model.JsonRpcResponse.JsonRpcError(id, code, message) - is Sign.Model.JsonRpcResponse.JsonRpcResult -> Modal.Model.JsonRpcResponse.JsonRpcResult(id, result) -} - -@JvmSynthetic -internal fun Sign.Model.SessionAuthenticateResponse.toModal(): Modal.Model.SessionAuthenticateResponse = - when (this) { - is Sign.Model.SessionAuthenticateResponse.Result -> Modal.Model.SessionAuthenticateResponse.Result(id, cacaos.toClient(), session?.toModal()) - is Sign.Model.SessionAuthenticateResponse.Error -> Modal.Model.SessionAuthenticateResponse.Error(id, code, message) - } - -@JvmSynthetic -internal fun Sign.Model.ExpiredProposal.toModal(): Modal.Model.ExpiredProposal = Modal.Model.ExpiredProposal(pairingTopic, proposerPublicKey) - -@JvmSynthetic -internal fun Sign.Model.ExpiredRequest.toModal(): Modal.Model.ExpiredRequest = Modal.Model.ExpiredRequest(topic, id) - - -@JvmSynthetic -internal fun List.toClient(): List = this.map { - with(it) { - Modal.Model.Cacao( - Modal.Model.Cacao.Header(header.t), - Modal.Model.Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Modal.Model.Cacao.Signature(signature.t, signature.s, signature.m) - ) - } -} - -internal fun Sign.Model.ConnectionState.toModal() = Modal.Model.ConnectionState(isAvailable) - -internal fun Sign.Model.Error.toModal() = Modal.Model.Error(throwable) - -internal fun Sign.Params.Disconnect.toModal() = Modal.Params.Disconnect(sessionTopic) - -internal fun Sign.Model.SentRequest.toModal() = Modal.Model.SentRequest(requestId, sessionTopic, method, params, chainId) - -internal fun Sign.Model.Ping.Success.toModal() = Modal.Model.Ping.Success(topic) - -internal fun Sign.Model.Ping.Error.toModal() = Modal.Model.Ping.Error(error) - -// toSign() -internal fun Modal.Params.Connect.toSign() = Sign.Params.Connect(namespaces?.toSign(), optionalNamespaces?.toSign(), properties, pairing) - -internal fun Modal.Params.Authenticate.toSign(): Sign.Params.Authenticate = with(this) { - Sign.Params.Authenticate( - pairingTopic, - chains = chains, - domain = domain, - uri = uri, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - methods = methods, - expiry = expiry - ) -} - -internal fun Modal.Model.AuthPayloadParams.toModel(pairingTopic: String): Modal.Params.Authenticate = with(this) { - Modal.Params.Authenticate( - chains = chains, - domain = domain, - uri = uri, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - expiry = expiry, - methods = methods, - pairingTopic = pairingTopic, - ) -} - -internal fun Modal.Model.AuthPayloadParams.toSign(issuer: String): Sign.Params.FormatMessage = with(this) { - Sign.Params.FormatMessage( - payloadParams = Sign.Model.PayloadParams( - chains = chains, - domain = domain, - aud = uri, - nonce = nonce, - nbf = nbf, - exp = exp, - iat = SimpleDateFormat(Cacao.Payload.ISO_8601_PATTERN).format(Calendar.getInstance().time), - type = "", - statement = statement, - requestId = requestId, - resources = resources, - ), - iss = issuer - ) -} - - -internal fun Map.toSign() = mapValues { (_, namespace) -> Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) } - -internal fun Modal.Params.Disconnect.toSign() = Sign.Params.Disconnect(sessionTopic) - -internal fun Modal.Params.Ping.toSign() = Sign.Params.Ping(topic) - -internal fun Request.toSign(sessionTopic: String, chainId: String) = Sign.Params.Request(sessionTopic, method, params, chainId, expiry) - -internal fun Modal.Listeners.SessionPing.toSign() = object : Sign.Listeners.SessionPing { - override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { - this@toSign.onSuccess(pingSuccess.toModal()) - } - - override fun onError(pingError: Sign.Model.Ping.Error) { - this@toSign.onError(pingError.toModal()) - } -} - -internal fun Sign.Model.Session.toSession() = Session.WalletConnectSession(pairingTopic, topic, expiry, namespaces.toModal(), metaData) - -internal fun Account.toCoinbaseSession() = Session.CoinbaseSession(chain.id, address) \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Modal.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Modal.kt deleted file mode 100644 index 950a1f73f..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Modal.kt +++ /dev/null @@ -1,272 +0,0 @@ -package com.walletconnect.web3.modal.client - -import androidx.annotation.DrawableRes -import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.internal.common.signing.cacao.Issuer - -object Modal { - - sealed interface Listeners { - interface SessionPing : Listeners { - fun onSuccess(pingSuccess: Model.Ping.Success) - fun onError(pingError: Model.Ping.Error) - } - } - - sealed class Params { - data class Init( - val core: CoreInterface, - val excludedWalletIds: List = listOf(), - val recommendedWalletsIds: List = listOf(), - val coinbaseEnabled: Boolean = true, - val enableAnalytics: Boolean? = null - ) : Params() - - data class Connect( - val namespaces: Map? = null, - val optionalNamespaces: Map? = null, - val properties: Map? = null, - val pairing: Core.Model.Pairing, - ) : Params() - - data class Authenticate( - val pairingTopic: String? = null, - val chains: List, - val domain: String, - val nonce: String, - val uri: String, - val nbf: String? = null, - val exp: String? = null, - val statement: String? = null, - val requestId: String? = null, - val resources: List? = null, - val methods: List? = null, - val expiry: Long? = null - ) : Params() - - @Deprecated( - message = "This has become deprecate in favor of the parameterless disconnect function", - level = DeprecationLevel.WARNING - ) - data class Disconnect(val sessionTopic: String) : Params() - - data class Ping(val topic: String) : Params() - - @Deprecated( - message = "Converted to sealed class to support multiple connectors", - replaceWith = ReplaceWith("com.walletconnect.web3.modal.client.models.Request"), - level = DeprecationLevel.WARNING - ) - data class Request( - val method: String, - val params: String, - val expiry: Long? = null, - ) : Params() - - data class SessionParams( - val requiredNamespaces: Map, - val optionalNamespaces: Map? = null, - val properties: Map? = null - ) - } - - sealed class Model { - data class Error(val throwable: Throwable) : Model() - - sealed class Namespace : Model() { - data class Proposal( - val chains: List? = null, - val methods: List, - val events: List, - ) : Namespace() - - data class Session( - val chains: List? = null, - val accounts: List, - val methods: List, - val events: List, - ) : Namespace() - } - - data class AuthPayloadParams( - val chains: List, - val domain: String, - val nonce: String, - val uri: String, - val nbf: String? = null, - val exp: String? = null, - val statement: String? = null, - val requestId: String? = null, - val resources: List? = null, - val methods: List? = null, - val expiry: Long? = null - ) : Model() - - sealed class ApprovedSession : Model() { - data class WalletConnectSession( - val topic: String, - val metaData: Core.Model.AppMetaData?, - val namespaces: Map, - val accounts: List, - ) : ApprovedSession() - - data class CoinbaseSession( - val chain: String, - val networkId: String, - val address: String - ) : ApprovedSession() - } - - data class RejectedSession(val topic: String, val reason: String) : Model() - - data class UpdatedSession( - val topic: String, - val namespaces: Map, - ) : Model() - - data class SessionEvent( - val name: String, - val data: String, - ) : Model() - - data class Event( - val topic: String, - val name: String, - val data: String, - val chainId: String, - ) : Model() - - sealed class DeletedSession : Model() { - data class Success(val topic: String, val reason: String) : DeletedSession() - data class Error(val error: Throwable) : DeletedSession() - } - - data class Session( - val pairingTopic: String, - val topic: String, - val expiry: Long, - val namespaces: Map, - val metaData: Core.Model.AppMetaData?, - ) : Model() { - val redirect: String? = metaData?.redirect - } - - data class SessionRequestResponse( - val topic: String, - val chainId: String?, - val method: String, - val result: JsonRpcResponse, - ) : Model() - - data class ConnectionState( - val isAvailable: Boolean, - ) : Model() - - data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : Model() - data class ExpiredRequest(val topic: String, val id: Long) : Model() - - sealed class JsonRpcResponse : Model() { - abstract val id: Long - val jsonrpc: String = "2.0" - - data class JsonRpcResult( - override val id: Long, - val result: String, - ) : JsonRpcResponse() - - data class JsonRpcError( - override val id: Long, - val code: Int, - val message: String, - ) : JsonRpcResponse() - } - - @Deprecated( - message = "Converted to sealed class to support multiple connectors", - replaceWith = ReplaceWith("com.walletconnect.web3.modal.client.models.SentRequestResult"), - level = DeprecationLevel.WARNING - ) - data class SentRequest( - val requestId: Long, - val sessionTopic: String, - val method: String, - val params: String, - val chainId: String, - ) : Model() - - sealed class SessionAuthenticateResponse : Model() { - data class Result(val id: Long, val cacaos: List, val session: Session?) : SessionAuthenticateResponse() - data class Error(val id: Long, val code: Int, val message: String) : SessionAuthenticateResponse() - } - - sealed class SIWEAuthenticateResponse : Model() { - data class Result(val id: Long, val message: String, val signature: String) : SIWEAuthenticateResponse() - data class Error(val id: Long, val code: Int, val message: String) : SIWEAuthenticateResponse() - } - - data class Cacao( - val header: Header, - val payload: Payload, - val signature: Signature, - ) : Model() { - @Keep - data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface - data class Header(val t: String) : Model() - data class Payload( - val iss: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() { - val address: String get() = Issuer(iss).address - } - } - - sealed class Ping : Model() { - data class Success(val topic: String) : Ping() - data class Error(val error: Throwable) : Ping() - } - - data class Chain( - val chainName: String, - val chainNamespace: String, - val chainReference: String, - val requiredMethods: List, - val optionalMethods: List, - val events: List, - val token: Token, - val chainImage: ChainImage? = null, - val rpcUrl: String? = null, - val blockExplorerUrl: String? = null - ) { - val id: String = "$chainNamespace:$chainReference" - } - - sealed class ChainImage { - data class Asset(@DrawableRes val id: Int) : ChainImage() - - data class Network(val url: String) : ChainImage() - } - - data class Token( - val name: String, - val symbol: String, - val decimal: Int - ) - } - - enum class ConnectorType { - WALLET_CONNECT, - COINBASE - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Web3Modal.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Web3Modal.kt deleted file mode 100644 index ec0c89c81..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/Web3Modal.kt +++ /dev/null @@ -1,350 +0,0 @@ -package com.walletconnect.web3.modal.client - -import androidx.activity.ComponentActivity -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.common.exceptions.SignClientAlreadyInitializedException -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.models.Account -import com.walletconnect.web3.modal.client.models.Session -import com.walletconnect.web3.modal.client.models.Web3ModelClientAlreadyInitializedException -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.client.models.request.SentRequestResult -import com.walletconnect.web3.modal.di.web3ModalModule -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.domain.model.Session.WalletConnect -import com.walletconnect.web3.modal.domain.model.toModalError -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import org.jetbrains.annotations.ApiStatus.Experimental -import org.koin.core.qualifier.named -import org.koin.dsl.module - -object Web3Modal { - - internal var chains: List = listOf() - - internal var sessionProperties: Map? = null - - internal var selectedChain: Modal.Model.Chain? = null - - internal var authPayloadParams: Modal.Model.AuthPayloadParams? = null - - private lateinit var web3ModalEngine: Web3ModalEngine - - interface ModalDelegate { - fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) - fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) - fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) - - @Deprecated( - message = "Use onSessionEvent(Modal.Model.Event) instead. Using both will result in duplicate events.", - replaceWith = ReplaceWith(expression = "onEvent(event)") - ) - fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) - fun onSessionEvent(sessionEvent: Modal.Model.Event) {} - fun onSessionExtend(session: Modal.Model.Session) - fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) - - //Responses - fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) - fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Modal.Model.SessionAuthenticateResponse) {} - fun onSIWEAuthenticationResponse(response: Modal.Model.SIWEAuthenticateResponse) {} - - // Utils - fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) - fun onRequestExpired(request: Modal.Model.ExpiredRequest) - fun onConnectionStateChange(state: Modal.Model.ConnectionState) - fun onError(error: Modal.Model.Error) - } - - interface ComponentDelegate { - fun onModalExpanded() - - fun onModalHidden() - - } - - fun initialize( - init: Modal.Params.Init, - onSuccess: () -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - SignClient.initialize( - init = Sign.Params.Init(init.core), - onSuccess = { - onInitializedClient(init, onSuccess, onError) - }, - onError = { error -> - if (error.throwable is SignClientAlreadyInitializedException) { - onInitializedClient(init, onSuccess, onError) - } else { - return@initialize onError(Modal.Model.Error(error.throwable)) - } - } - ) - } - - @Experimental - fun register(activity: ComponentActivity) { - checkEngineInitialization() - web3ModalEngine.registerCoinbaseLauncher(activity) - } - - @Experimental - fun unregister() { - checkEngineInitialization() - web3ModalEngine.unregisterCoinbase() - } - - @Throws(IllegalStateException::class) - private fun checkEngineInitialization() { - check(::web3ModalEngine.isInitialized) { - "Web3Modal needs to be initialized first using the initialize function" - } - } - - private fun onInitializedClient( - init: Modal.Params.Init, - onSuccess: () -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - if (!::web3ModalEngine.isInitialized) { - runCatching { - wcKoinApp.modules(web3ModalModule()) - web3ModalEngine = wcKoinApp.koin.get() - web3ModalEngine.setup(init, onError) - web3ModalEngine.setInternalDelegate(Web3ModalDelegate) - wcKoinApp.modules( - module { single(named(AndroidCommonDITags.ENABLE_WEB_3_MODAL_ANALYTICS)) { init.enableAnalytics ?: web3ModalEngine.fetchAnalyticsConfig() } } - ) - } - .onFailure { error -> return@onInitializedClient onError(Modal.Model.Error(error)) } - .onSuccess { - onSuccess() - web3ModalEngine.send(Props(event = EventType.TRACK, type = EventType.Track.MODAL_LOADED)) - } - } else { - onError(Modal.Model.Error(Web3ModelClientAlreadyInitializedException())) - } - } - - fun setChains(chains: List) { - this.chains = chains - } - - fun setAuthRequestParams(authParams: Modal.Model.AuthPayloadParams) { - authPayloadParams = authParams - } - - fun setSessionProperties(properties: Map) { - sessionProperties = properties - } - - @Throws(IllegalStateException::class) - fun setDelegate(delegate: ModalDelegate) { - Web3ModalDelegate.connectionState.onEach { connectionState -> - delegate.onConnectionStateChange(connectionState) - }.launchIn(scope) - - Web3ModalDelegate.wcEventModels.onEach { event -> - when (event) { - is Modal.Model.ApprovedSession -> delegate.onSessionApproved(event) - is Modal.Model.DeletedSession.Success -> delegate.onSessionDelete(event) - is Modal.Model.Error -> delegate.onError(event) - is Modal.Model.RejectedSession -> delegate.onSessionRejected(event) - is Modal.Model.Session -> delegate.onSessionExtend(event) - //todo: how to notify developer to not us both at the same time - is Modal.Model.SessionEvent -> delegate.onSessionEvent(event) - is Modal.Model.Event -> delegate.onSessionEvent(event) - is Modal.Model.SessionRequestResponse -> delegate.onSessionRequestResponse(event) - is Modal.Model.UpdatedSession -> delegate.onSessionUpdate(event) - is Modal.Model.ExpiredRequest -> delegate.onRequestExpired(event) - is Modal.Model.ExpiredProposal -> delegate.onProposalExpired(event) - is Modal.Model.SessionAuthenticateResponse -> delegate.onSessionAuthenticateResponse(event) - is Modal.Model.SIWEAuthenticateResponse -> delegate.onSIWEAuthenticationResponse(event) - else -> Unit - } - }.launchIn(scope) - } - - @Deprecated( - message = "Replaced with the same name method but onSuccess callback returns a Pairing URL", - replaceWith = ReplaceWith(expression = "fun connect(connect: Modal.Params.Connect, onSuccess: (String) -> Unit, onError: (Modal.Model.Error) -> Unit)") - ) - internal fun connect( - connect: Modal.Params.Connect, - onSuccess: () -> Unit, - onError: (Modal.Model.Error) -> Unit - ) { - SignClient.connect( - connect.toSign(), - onSuccess - ) { onError(it.toModal()) } - } - - fun connect( - connect: Modal.Params.Connect, - onSuccess: (String) -> Unit, - onError: (Modal.Model.Error) -> Unit - ) { - SignClient.connect( - connect = connect.toSign(), - onSuccess = { url -> onSuccess(url) }, - onError = { onError(it.toModal()) } - ) - } - - fun authenticate( - authenticate: Modal.Params.Authenticate, - walletAppLink: String? = null, - onSuccess: (String) -> Unit, - onError: (Modal.Model.Error) -> Unit, - ) { - - SignClient.authenticate(authenticate.toSign(), walletAppLink, - onSuccess = { url -> onSuccess(url) }, - onError = { onError(it.toModal()) }) - } - - fun handleDeepLink(url: String, onError: (Modal.Model.Error) -> Unit) { - SignClient.dispatchEnvelope(url) { - onError(it.toModal()) - } - } - - @Deprecated( - message = "Modal.Params.Request is deprecated", - replaceWith = ReplaceWith("com.walletconnect.web3.modal.client.models.Request") - ) - fun request( - request: Modal.Params.Request, - onSuccess: (Modal.Model.SentRequest) -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - checkEngineInitialization() - web3ModalEngine.request( - request = Request(request.method, request.params, request.expiry), - onSuccess = { onSuccess(it.sentRequestToModal()) }, - onError = { onError(it.toModalError()) } - ) - } - - fun request( - request: Request, - onSuccess: (SentRequestResult) -> Unit = {}, - onError: (Throwable) -> Unit, - ) { - checkEngineInitialization() - web3ModalEngine.request(request, onSuccess, onError) - } - - private fun SentRequestResult.sentRequestToModal() = when (this) { - is SentRequestResult.Coinbase -> Modal.Model.SentRequest(Long.MIN_VALUE, String.Empty, method, params, chainId) - is SentRequestResult.WalletConnect -> Modal.Model.SentRequest(requestId, sessionTopic, method, params, chainId) - } - - fun request( - request: Request, - onSuccess: () -> Unit, - onError: (Throwable) -> Unit, - ) { - checkEngineInitialization() - web3ModalEngine.request(request, { onSuccess() }, onError) - } - - fun ping(sessionPing: Modal.Listeners.SessionPing? = null) = web3ModalEngine.ping(sessionPing) - - @Deprecated( - message = "This has become deprecate in favor of the parameterless disconnect function", - level = DeprecationLevel.WARNING - ) - fun disconnect( - onSuccess: (Modal.Params.Disconnect) -> Unit = {}, - onError: (Modal.Model.Error) -> Unit, - ) { - checkEngineInitialization() - val topic = when (val session = web3ModalEngine.getActiveSession()) { - is WalletConnect -> session.topic - else -> String.Empty - } - - web3ModalEngine.disconnect( - onSuccess = { onSuccess(Modal.Params.Disconnect(topic)) }, - onError = { onError(it.toModalError()) } - ) - } - - fun disconnect( - onSuccess: () -> Unit, - onError: (Throwable) -> Unit, - ) { - checkEngineInitialization() - web3ModalEngine.disconnect(onSuccess, onError) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getSelectedChain() = selectedChain -// fun getSelectedChain() = getSelectedChainUseCase()?.toChain() - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - message = "Getting active session is replaced with getAccount()", - replaceWith = ReplaceWith("com.walletconnect.web3.modal.client.Web3Modal.getAccount()"), - level = DeprecationLevel.WARNING - ) - internal fun getActiveSessionByTopic(topic: String) = SignClient.getActiveSessionByTopic(topic)?.toModal() - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - message = "Getting active session is replaced with getAccount()", - replaceWith = ReplaceWith("com.walletconnect.web3.modal.client.Web3Modal.getAccount()"), - level = DeprecationLevel.WARNING - ) - fun getActiveSession(): Modal.Model.Session? { - checkEngineInitialization() - return (web3ModalEngine.getActiveSession() as? WalletConnect)?.topic?.let { SignClient.getActiveSessionByTopic(it)?.toModal() } - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getAccount(): Account? { - checkEngineInitialization() - return web3ModalEngine.getAccount() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getSession(): Session? { - checkEngineInitialization() - return web3ModalEngine.getSession() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getConnectorType(): Modal.ConnectorType? { - checkEngineInitialization() - return web3ModalEngine.getConnectorType() - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Account.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Account.kt deleted file mode 100644 index 7a45f221e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Account.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.web3.modal.client.models - -import com.walletconnect.web3.modal.client.Modal - -data class Account( - val address: String, - val chain: Modal.Model.Chain -) \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Exceptions.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Exceptions.kt deleted file mode 100644 index b6eb6d753..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Exceptions.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.web3.modal.client.models - -import com.walletconnect.android.internal.common.exception.WalletConnectException - -class Web3ModelClientAlreadyInitializedException : WalletConnectException("Web3Modal already initialized") -class CoinbaseClientAlreadyInitializedException : WalletConnectException("Coinbase already initialized") \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Session.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Session.kt deleted file mode 100644 index 7eaefc2e0..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/Session.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.walletconnect.web3.modal.client.models - -import com.walletconnect.android.Core -import com.walletconnect.web3.modal.client.Modal - -sealed class Session { - - data class WalletConnectSession( - val pairingTopic: String, - val topic: String, - val expiry: Long, - val namespaces: Map, - val metaData: Core.Model.AppMetaData?, - ) : Session() { - val redirect: String? = metaData?.redirect - } - - data class CoinbaseSession( - val chain: String, - val address: String - ) : Session() -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/Request.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/Request.kt deleted file mode 100644 index 94f9aec12..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/client/models/request/Request.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.web3.modal.client.models.request - -data class Request( - val method: String, - val params: String, - val expiry: Long? = null, -) \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BalanceRpcRepository.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BalanceRpcRepository.kt deleted file mode 100644 index 4e3875361..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BalanceRpcRepository.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.walletconnect.web3.modal.data - -import com.walletconnect.foundation.util.Logger -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.data.json_rpc.balance.BalanceRequest -import com.walletconnect.web3.modal.data.json_rpc.balance.BalanceRpcResponse -import com.walletconnect.web3.modal.data.network.BalanceService -import com.walletconnect.web3.modal.domain.model.Balance - -internal class BalanceRpcRepository( - private val balanceService: BalanceService, - private val logger: Logger, -) { - - suspend fun getBalance( - token: Modal.Model.Token, rpcUrl: String, address: String - ) = runCatching { - balanceService.getBalance( - url = rpcUrl, body = BalanceRequest(address = address) - ) - }.mapCatching { response -> - response.body()!!.mapResponse(token) - }.onFailure { - logger.error(it) - }.getOrNull() -} - -private fun BalanceRpcResponse.mapResponse(token: Modal.Model.Token) = when { - result != null -> Balance(token, result) - error != null -> throw Throwable(error.message) - else -> throw Throwable("Invalid balance response") -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BlockchainRepository.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BlockchainRepository.kt deleted file mode 100644 index 67455f44e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/BlockchainRepository.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.web3.modal.data - -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.web3.modal.data.model.IdentityDTO -import com.walletconnect.web3.modal.data.network.BlockchainService -import com.walletconnect.web3.modal.domain.model.Identity - -internal class BlockchainRepository( - private val blockchainService: BlockchainService, - private val projectId: ProjectId -) { - - suspend fun getIdentity(address: String, chainId: String) = with( - blockchainService.getIdentity(address = address, chainId = chainId, projectId = projectId.value) - ) { - if (isSuccessful && body() != null) { - body()!!.toIdentity() - } else { - throw Throwable(errorBody()?.string()) - } - } -} - -private fun IdentityDTO.toIdentity() = Identity(name, avatar) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/JsonRpcMethod.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/JsonRpcMethod.kt deleted file mode 100644 index 78aa3cbc8..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/JsonRpcMethod.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.web3.modal.data.json_rpc - -internal object JsonRpcMethod { - @get:JvmSynthetic - const val ETH_GET_BALANCE: String = "eth_getBalance" -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpc.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpc.kt deleted file mode 100644 index 3c31bfa89..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/json_rpc/balance/BalanceRpc.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.web3.modal.data.json_rpc.balance - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.util.generateId -import com.walletconnect.web3.modal.data.json_rpc.JsonRpcMethod - -@JsonClass(generateAdapter = true) -internal data class BalanceRequest( - val address: String, - @Json(name = "id") - val id: Long = generateId(), - @Json(name = "jsonrpc") - val jsonrpc: String = "2.0", - @Json(name = "method") - val method: String = JsonRpcMethod.ETH_GET_BALANCE, - @Json(name = "params") - val params: List = listOf(address, "latest") -) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BalanceService.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BalanceService.kt deleted file mode 100644 index 14a7a0eb8..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/data/network/BalanceService.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.web3.modal.data.network - -import com.walletconnect.web3.modal.data.json_rpc.balance.BalanceRequest -import com.walletconnect.web3.modal.data.json_rpc.balance.BalanceRpcResponse -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.POST -import retrofit2.http.Url - -internal interface BalanceService { - - @POST - suspend fun getBalance( - @Url url: String, - @Body body: BalanceRequest - ): Response -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BalanceRpcModule.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BalanceRpcModule.kt deleted file mode 100644 index b1f9195d4..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BalanceRpcModule.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.web3.modal.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.web3.modal.data.BalanceRpcRepository -import com.walletconnect.web3.modal.data.network.BalanceService -import com.walletconnect.web3.modal.domain.usecase.GetEthBalanceUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -internal fun balanceRpcModule() = module { - - single(named(Web3ModalDITags.BALANCE_RPC_RETROFIT)) { - Retrofit.Builder() - // Passing url to google to passing retrofit verification. The correct url to chain RPC is provided on the BalanceService::class - .baseUrl("https://google.com/") - .client(get(named(AndroidCommonDITags.OK_HTTP))) - .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) - .build() - } - - single { get(named(Web3ModalDITags.BALANCE_RPC_RETROFIT)).create(BalanceService::class.java) } - - single { BalanceRpcRepository(balanceService = get(), logger = get()) } - - single { GetEthBalanceUseCase(balanceRpcRepository = get()) } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BlockchainApiModule.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BlockchainApiModule.kt deleted file mode 100644 index db5d93659..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/BlockchainApiModule.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.walletconnect.web3.modal.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.web3.modal.data.BlockchainRepository -import com.walletconnect.web3.modal.data.network.BlockchainService -import com.walletconnect.web3.modal.domain.usecase.GetIdentityUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -internal fun blockchainApiModule() = module { - - single(named(Web3ModalDITags.BLOCKCHAIN_RETROFIT)) { - Retrofit.Builder() - .baseUrl("https://rpc.walletconnect.org/v1/") - .client(get(named(AndroidCommonDITags.OK_HTTP))) - .addConverterFactory(MoshiConverterFactory.create(get(named(AndroidCommonDITags.MOSHI)))) - .build() - } - - single { - get(named(Web3ModalDITags.BLOCKCHAIN_RETROFIT)).create(BlockchainService::class.java) - } - - single { - BlockchainRepository(blockchainService = get(), projectId = get()) - } - - single { GetIdentityUseCase(blockchainRepository = get()) } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/EngineModule.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/EngineModule.kt deleted file mode 100644 index ef25b9103..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/EngineModule.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.walletconnect.web3.modal.di - -import android.content.Context -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.web3.modal.domain.usecase.ConnectionEventRepository -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import com.walletconnect.web3.modal.engine.coinbase.CoinbaseClient -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.dsl.module - -internal fun engineModule() = module { - - single { - ConnectionEventRepository(sharedPreferences = androidContext().getSharedPreferences("ConnectionEvents", Context.MODE_PRIVATE)) - } - - single { - Web3ModalEngine( - getSessionUseCase = get(), - getSelectedChainUseCase = get(), - deleteSessionDataUseCase = get(), - saveSessionUseCase = get(), - connectionEventRepository = get(), - enableAnalyticsUseCase = get(), - sendEventUseCase = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - ) - } - single { - CoinbaseClient( - context = get(), - appMetaData = get() - ) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalDITags.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalDITags.kt deleted file mode 100644 index 24db155f5..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalDITags.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.web3.modal.di - -internal enum class Web3ModalDITags { - BALANCE_RPC_RETROFIT, - BLOCKCHAIN_RETROFIT, - MOSHI, - SESSION_DATA_STORE, -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalModule.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalModule.kt deleted file mode 100644 index 6b9dabf19..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/di/Web3ModalModule.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.walletconnect.web3.modal.di - -import android.content.Context -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.preferencesDataStore -import com.squareup.moshi.Moshi -import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.web3.modal.domain.RecentWalletsRepository -import com.walletconnect.web3.modal.domain.SessionRepository -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.DeleteSessionDataUseCase -import com.walletconnect.web3.modal.domain.usecase.GetRecentWalletUseCase -import com.walletconnect.web3.modal.domain.usecase.GetSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.GetSessionUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSessionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveChainSelectionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveRecentWalletUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveSessionUseCase -import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named -import org.koin.dsl.module - -private val Context.sessionDataStore: DataStore by preferencesDataStore(name = "session_store") - -internal fun web3ModalModule() = module { - - single { RecentWalletsRepository(sharedPreferences = get()) } - - single { GetRecentWalletUseCase(repository = get()) } - single { SaveRecentWalletUseCase(repository = get()) } - - single> { - PolymorphicJsonAdapterFactory.of(Session::class.java, "type") - .withSubtype(Session.WalletConnect::class.java, "wcsession") - .withSubtype(Session.Coinbase::class.java, "coinbase") - } - - single(named(Web3ModalDITags.MOSHI)) { - get(named(AndroidCommonDITags.MOSHI)) - .add(get>()) - .build() - } - - single(named(Web3ModalDITags.SESSION_DATA_STORE)) { androidContext().sessionDataStore } - single { SessionRepository(sessionStore = get(named(Web3ModalDITags.SESSION_DATA_STORE)), moshi = get(named(Web3ModalDITags.MOSHI))) } - - single { GetSessionUseCase(repository = get()) } - single { SaveSessionUseCase(repository = get()) } - single { DeleteSessionDataUseCase(repository = get()) } - single { SaveChainSelectionUseCase(repository = get()) } - single { GetSelectedChainUseCase(repository = get()) } - single { ObserveSessionUseCase(repository = get()) } - single { ObserveSelectedChainUseCase(repository = get()) } - - includes(blockchainApiModule(), balanceRpcModule(), engineModule()) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/RecentWalletsRepository.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/RecentWalletsRepository.kt deleted file mode 100644 index fb6c8fbea..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/RecentWalletsRepository.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.web3.modal.domain - -import android.content.SharedPreferences - -private const val RECENT_WALLET_ID = "w3m_recent_wallet_id" - -internal class RecentWalletsRepository( - private val sharedPreferences: SharedPreferences -) { - - fun saveRecentWalletId(id: String) = sharedPreferences.edit().putString(RECENT_WALLET_ID, id).apply() - - fun getRecentWalletId() = sharedPreferences.getString(RECENT_WALLET_ID, null) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/delegate/Web3ModalDelegate.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/delegate/Web3ModalDelegate.kt deleted file mode 100644 index 6be3977b9..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/delegate/Web3ModalDelegate.kt +++ /dev/null @@ -1,181 +0,0 @@ -package com.walletconnect.web3.modal.domain.delegate - -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.DeleteSessionDataUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveChainSelectionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveSessionUseCase -import com.walletconnect.web3.modal.utils.EthUtils -import com.walletconnect.web3.modal.utils.getAddress -import com.walletconnect.web3.modal.utils.getSelectedChain -import com.walletconnect.web3.modal.utils.toSession -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal object Web3ModalDelegate : Web3Modal.ModalDelegate { - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() - val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() - - private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) - val connectionState: SharedFlow = _connectionState.asSharedFlow() - - //todo replace it with engine - private val saveSessionUseCase: SaveSessionUseCase by lazy { wcKoinApp.koin.get() } - private val saveChainSelectionUseCase: SaveChainSelectionUseCase by lazy { wcKoinApp.koin.get() } - private val deleteSessionDataUseCase: DeleteSessionDataUseCase by lazy { wcKoinApp.koin.get() } - - fun emit(event: Modal.Model?) { - scope.launch { - _wcEventModels.emit(event) - } - } - - override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { - scope.launch { - val chain = Web3Modal.chains.getSelectedChain(Web3Modal.selectedChain?.id) - saveSessionUseCase(approvedSession.toSession(chain)) - _wcEventModels.emit(approvedSession) - } - } - - override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Modal.Model.SessionAuthenticateResponse) { - scope.launch { - if (sessionAuthenticateResponse is Modal.Model.SessionAuthenticateResponse.Result) { - val chain = Web3Modal.chains.getSelectedChain(Web3Modal.selectedChain?.id) - saveSessionUseCase( - Session.WalletConnect( - chain = chain.id, - topic = sessionAuthenticateResponse.session?.topic ?: "", - address = sessionAuthenticateResponse.session?.getAddress(chain) ?: "" - ) - ) - } - - _wcEventModels.emit(sessionAuthenticateResponse) - } - } - - override fun onSIWEAuthenticationResponse(response: Modal.Model.SIWEAuthenticateResponse) { - scope.launch { - _wcEventModels.emit(response) - } - } - - override fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) { - scope.launch { - _wcEventModels.emit(rejectedSession) - } - } - - override fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) { - scope.launch { - val chain = Web3Modal.chains.getSelectedChain(Web3Modal.selectedChain?.id) - saveSessionUseCase(updatedSession.toSession(chain)) - _wcEventModels.emit(updatedSession) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) { - scope.launch { - consumeSessionEvent(sessionEvent) - _wcEventModels.emit(sessionEvent) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.Event) { - scope.launch { - consumeEvent(sessionEvent) - _wcEventModels.emit(sessionEvent) - } - } - - private suspend fun consumeSessionEvent(sessionEvent: Modal.Model.SessionEvent) { - try { - when (sessionEvent.name) { - EthUtils.accountsChanged -> { - val (_, chainReference, _) = sessionEvent.data.split(":") - Web3Modal.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } - } - - EthUtils.chainChanged -> { - val (chainReference, _) = sessionEvent.data.split(".") - Web3Modal.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } - } - } - } catch (throwable: Throwable) { - onError(Modal.Model.Error(throwable)) - } - } - - //todo: Do we need to change anything here? We have more data in event now. - private suspend fun consumeEvent(event: Modal.Model.Event) { - try { - when (event.name) { - EthUtils.accountsChanged -> { - //todo: Can we take chainReference from the event? - val (_, chainReference, _) = event.data.split(":") - Web3Modal.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } - } - - EthUtils.chainChanged -> { - //todo: Can we take chainReference from the event? - val (chainReference, _) = event.data.split(".") - Web3Modal.chains.find { it.chainReference == chainReference }?.let { chain -> saveChainSelectionUseCase(chain.id) } - } - } - } catch (throwable: Throwable) { - onError(Modal.Model.Error(throwable)) - } - } - - override fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) { - scope.launch { - deleteSessionDataUseCase() - _wcEventModels.emit(deletedSession) - } - } - - override fun onSessionExtend(session: Modal.Model.Session) { - scope.launch { - _wcEventModels.emit(session) - } - } - - override fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) { - scope.launch { - _wcEventModels.emit(response) - } - } - - override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) { - scope.launch { - _wcEventModels.emit(proposal) - } - } - - override fun onRequestExpired(request: Modal.Model.ExpiredRequest) { - scope.launch { - _wcEventModels.emit(request) - } - } - - override fun onConnectionStateChange(state: Modal.Model.ConnectionState) { - scope.launch { - _connectionState.emit(state) - } - } - - override fun onError(error: Modal.Model.Error) { - scope.launch { - _wcEventModels.emit(error) - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/AccountData.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/AccountData.kt deleted file mode 100644 index 6de7345dc..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/AccountData.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.web3.modal.domain.model - -import com.walletconnect.web3.modal.client.Modal - -internal data class AccountData( - val address: String, - val chains: List, - val identity: Identity? = null -) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Exceptions.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Exceptions.kt deleted file mode 100644 index 0413eefdf..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Exceptions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.web3.modal.domain.model - -import com.walletconnect.web3.modal.client.Modal - -object InvalidSessionException: Throwable("Session topic is missing") - -internal fun Throwable.toModalError() = Modal.Model.Error(this) \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Identity.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Identity.kt deleted file mode 100644 index 2bb8ebef8..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Identity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.web3.modal.domain.model - -data class Identity( - val name: String? = null, - val avatar: String? = null -) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Session.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Session.kt deleted file mode 100644 index f8c4fda20..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/model/Session.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.walletconnect.web3.modal.domain.model - -import com.squareup.moshi.JsonClass - -internal sealed class Session { - abstract val address: String - abstract val chain: String - - @JsonClass(generateAdapter = true) - data class WalletConnect( - override val address: String, - override val chain: String, - val topic: String, - ) : Session() - - @JsonClass(generateAdapter = true) - data class Coinbase( - override val chain: String, - override val address: String, - ) : Session() -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/DeleteSessionDataUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/DeleteSessionDataUseCase.kt deleted file mode 100644 index 3919968ec..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/DeleteSessionDataUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository - -internal class DeleteSessionDataUseCase( - private val repository: SessionRepository -) { - suspend operator fun invoke() { - repository.deleteSession() - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetEthBalanceUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetEthBalanceUseCase.kt deleted file mode 100644 index f13df64bb..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetEthBalanceUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.data.BalanceRpcRepository - -internal class GetEthBalanceUseCase( - private val balanceRpcRepository: BalanceRpcRepository, -) { - suspend operator fun invoke( - token: Modal.Model.Token, - rpcUrl: String, - address: String - ) = balanceRpcRepository.getBalance( - token = token, - rpcUrl = rpcUrl, - address = address - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetIdentityUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetIdentityUseCase.kt deleted file mode 100644 index 7d9da34e5..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetIdentityUseCase.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.data.BlockchainRepository -import com.walletconnect.web3.modal.domain.model.Identity - -internal class GetIdentityUseCase( - private val blockchainRepository: BlockchainRepository -) { - suspend operator fun invoke(address: String, chainId: String) = try { - blockchainRepository.getIdentity(address = address, chainId = chainId) - } catch (e: Throwable) { - null - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetRecentWalletUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetRecentWalletUseCase.kt deleted file mode 100644 index c197e059e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetRecentWalletUseCase.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.RecentWalletsRepository - -internal class GetRecentWalletUseCase(private val repository: RecentWalletsRepository) { - operator fun invoke() = repository.getRecentWalletId() -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSelectedChainUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSelectedChainUseCase.kt deleted file mode 100644 index cdfedd650..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSelectedChainUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking - -internal class GetSelectedChainUseCase( - private val repository: SessionRepository -) { - operator fun invoke() = runBlocking { repository.session.first()?.chain } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSessionUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSessionUseCase.kt deleted file mode 100644 index 478a93d44..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/GetSessionUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository -import kotlinx.coroutines.runBlocking - -internal class GetSessionUseCase( - private val repository: SessionRepository -) { - operator fun invoke() = runBlocking { repository.getSession() } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSelectedChainUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSelectedChainUseCase.kt deleted file mode 100644 index e9ac3fd9b..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSelectedChainUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository -import kotlinx.coroutines.flow.map - -internal class ObserveSelectedChainUseCase( - private val repository: SessionRepository -) { - operator fun invoke() = repository.session.map { it?.chain } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSessionUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSessionUseCase.kt deleted file mode 100644 index 25e7f5d4c..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/ObserveSessionUseCase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository - -internal class ObserveSessionUseCase( - private val repository: SessionRepository -) { - operator fun invoke() = repository.session -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveChainSelectionUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveChainSelectionUseCase.kt deleted file mode 100644 index 50a41cdb6..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveChainSelectionUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository - -internal class SaveChainSelectionUseCase( - private val repository: SessionRepository -) { - suspend operator fun invoke(chain: String) { - repository.updateChainSelection(chain) - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveRecentWalletUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveRecentWalletUseCase.kt deleted file mode 100644 index e3e50ed9e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveRecentWalletUseCase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.RecentWalletsRepository - -internal class SaveRecentWalletUseCase( - private val repository: RecentWalletsRepository -) { - operator fun invoke(id: String) = repository.saveRecentWalletId(id) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveSessionUseCase.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveSessionUseCase.kt deleted file mode 100644 index 03100bab1..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/domain/usecase/SaveSessionUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.web3.modal.domain.usecase - -import com.walletconnect.web3.modal.domain.SessionRepository -import com.walletconnect.web3.modal.domain.model.Session - -internal class SaveSessionUseCase( - private val repository: SessionRepository -) { - suspend operator fun invoke(session: Session) = repository.saveSession(session) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/Web3ModalEngine.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/Web3ModalEngine.kt deleted file mode 100644 index 45364bc90..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/engine/Web3ModalEngine.kt +++ /dev/null @@ -1,353 +0,0 @@ -package com.walletconnect.web3.modal.engine - -import android.content.Context -import android.content.Intent -import android.net.Uri -import androidx.activity.ComponentActivity -import androidx.activity.result.contract.ActivityResultContracts -import com.walletconnect.android.internal.common.modal.domain.usecase.EnableAnalyticsUseCaseInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.client.models.Account -import com.walletconnect.web3.modal.client.models.CoinbaseClientAlreadyInitializedException -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.client.models.request.SentRequestResult -import com.walletconnect.web3.modal.client.models.request.toSentRequest -import com.walletconnect.web3.modal.client.toCoinbaseSession -import com.walletconnect.web3.modal.client.toModal -import com.walletconnect.web3.modal.client.toSession -import com.walletconnect.web3.modal.client.toSign -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.domain.model.InvalidSessionException -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.ConnectionEventRepository -import com.walletconnect.web3.modal.domain.usecase.DeleteSessionDataUseCase -import com.walletconnect.web3.modal.domain.usecase.GetSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.GetSessionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveSessionUseCase -import com.walletconnect.web3.modal.engine.coinbase.COINBASE_WALLET_ID -import com.walletconnect.web3.modal.engine.coinbase.CoinbaseClient -import com.walletconnect.web3.modal.utils.getSelectedChain -import com.walletconnect.web3.modal.utils.toAccount -import com.walletconnect.web3.modal.utils.toChain -import com.walletconnect.web3.modal.utils.toConnectorType -import com.walletconnect.web3.modal.utils.toSession -import kotlinx.coroutines.launch - -internal class Web3ModalEngine( - private val getSessionUseCase: GetSessionUseCase, - private val getSelectedChainUseCase: GetSelectedChainUseCase, - private val saveSessionUseCase: SaveSessionUseCase, - private val deleteSessionDataUseCase: DeleteSessionDataUseCase, - private val sendEventUseCase: SendEventInterface, - private val connectionEventRepository: ConnectionEventRepository, - private val enableAnalyticsUseCase: EnableAnalyticsUseCaseInterface, - private val logger: Logger -) : SendEventInterface by sendEventUseCase, - EnableAnalyticsUseCaseInterface by enableAnalyticsUseCase { - internal var excludedWalletsIds: MutableList = mutableListOf() - internal var recommendedWalletsIds: MutableList = mutableListOf() - internal var siweRequestIdWithMessage: Pair? = null - private lateinit var coinbaseClient: CoinbaseClient - internal var shouldDisconnect: Boolean = true - - fun setup( - init: Modal.Params.Init, - onError: (Modal.Model.Error) -> Unit - ) { - excludedWalletsIds.addAll(init.excludedWalletIds) - recommendedWalletsIds.addAll(init.recommendedWalletsIds) - setupCoinbase(init, onError) - } - - private fun setupCoinbase(init: Modal.Params.Init, onError: (Modal.Model.Error) -> Unit) { - if (init.coinbaseEnabled) { - if (!::coinbaseClient.isInitialized) { - coinbaseClient = CoinbaseClient(context = wcKoinApp.koin.get(), appMetaData = wcKoinApp.koin.get()) - } else { - onError(Modal.Model.Error(CoinbaseClientAlreadyInitializedException())) - } - } else { - excludedWalletsIds.add(COINBASE_WALLET_ID) - } - } - - fun connectWC( - name: String, method: String, - connect: Modal.Params.Connect, - onSuccess: (String) -> Unit, - onError: (Throwable) -> Unit - ) { - connectionEventRepository.saveEvent(name, method) - SignClient.connect(connect.toSign(), onSuccess) { onError(it.throwable) } - } - - fun authenticate( - name: String, method: String, - authenticate: Modal.Params.Authenticate, - walletAppLink: String? = null, - onSuccess: (String) -> Unit, - onError: (Throwable) -> Unit - ) { - connectionEventRepository.saveEvent(name, method) - SignClient.authenticate(authenticate.toSign(), walletAppLink, onSuccess) { onError(it.throwable) } - } - - fun connectCoinbase( - onSuccess: () -> Unit, - onError: (Throwable) -> Unit - ) { - checkEngineInitialization() - coinbaseClient.connect( - onSuccess = { - scope.launch { - val chain = Web3Modal.chains.getSelectedChain(Web3Modal.selectedChain?.id) - saveSessionUseCase(it.toSession(chain)) - Web3ModalDelegate.emit(it) - onSuccess() - } - - }, onError = { - onError(it) - } - ) - } - - fun getSelectedChain() = getSelectedChainUseCase()?.toChain() - - fun getActiveSession() = getSessionUseCase()?.isSessionActive() - - private fun Session.isSessionActive() = when (this) { - is Session.Coinbase -> if (coinbaseClient.isConnected()) this else null - is Session.WalletConnect -> SignClient.getActiveSessionByTopic(topic)?.let { this } - } - - fun getConnectorType() = getSessionUseCase()?.toConnectorType() - - internal fun getSelectedChainOrFirst() = getSelectedChain() ?: Web3Modal.chains.first() - - fun formatSIWEMessage(authParams: Modal.Model.AuthPayloadParams, issuer: String): String { - return SignClient.formatAuthMessage(authParams.toSign(issuer)) - } - - fun request(request: Request, onSuccess: (SentRequestResult) -> Unit, onError: (Throwable) -> Unit) { - val session = getActiveSession() - val selectedChain = getSelectedChain() - - if (session == null || selectedChain == null) { - onError(InvalidSessionException) - return - } - - when (session) { - is Session.Coinbase -> { - checkEngineInitialization() - coinbaseClient.request(request, { onSuccess(SentRequestResult.Coinbase(request.method, request.params, selectedChain.id, it)) }, onError) - } - - is Session.WalletConnect -> - SignClient.request(request.toSign(session.topic, selectedChain.id), - { - onSuccess(it.toSentRequest()) - openWalletApp(session.topic, onError) - }, - { onError(it.throwable) } - ) - } - } - - private fun openWalletApp(topic: String, onError: (RedirectMissingThrowable) -> Unit) { - val redirect = SignClient.getActiveSessionByTopic(topic)?.redirect ?: String.Empty - try { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(redirect)) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - wcKoinApp.koin.get().startActivity(intent) - } catch (e: Throwable) { - onError(RedirectMissingThrowable("Please redirect to a wallet manually")) - } - } - - fun ping(sessionPing: Modal.Listeners.SessionPing?) { - when (val session = getSessionUseCase()) { - is Session.WalletConnect -> SignClient.ping(Sign.Params.Ping(session.topic), sessionPing?.toSign()) - else -> sessionPing?.onError(Modal.Model.Ping.Error(InvalidSessionException)) - } - } - - fun disconnect(onSuccess: () -> Unit = {}, onError: (Throwable) -> Unit) { - val session = getSessionUseCase() - - if (session == null) { - onError(InvalidSessionException) - return - } - - when (session) { - is Session.Coinbase -> { - checkEngineInitialization() - coinbaseClient.disconnect() - scope.launch { deleteSessionDataUseCase() } - onSuccess() - } - - is Session.WalletConnect -> { - SignClient.disconnect(Sign.Params.Disconnect(session.topic), - onSuccess = { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.DISCONNECT_SUCCESS)) - scope.launch { deleteSessionDataUseCase() } - shouldDisconnect = true - onSuccess() - }, - onError = { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.DISCONNECT_ERROR)) - onError(it.throwable) - }) - } - } - } - - fun clearSession() { - scope.launch { deleteSessionDataUseCase() } - } - - fun getAccount(): Account? = getActiveSession()?.let { session -> - when (session) { - is Session.Coinbase -> coinbaseClient.getAccount(session) - is Session.WalletConnect -> SignClient.getActiveSessionByTopic(session.topic)?.toAccount(session) - } - } - - fun getSession() = getSessionUseCase()?.let { session -> - when (session) { - is Session.Coinbase -> coinbaseClient.getAccount(session)?.toCoinbaseSession() - is Session.WalletConnect -> SignClient.getActiveSessionByTopic(session.topic)?.toSession() - } - } - - @Throws(IllegalStateException::class) - fun setInternalDelegate(delegate: Web3ModalDelegate) { - val signDelegate = object : SignClient.DappDelegate { - override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) { - try { - val (name, method) = connectionEventRepository.getEvent() - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_SUCCESS, Properties(name = name, method = method))) - connectionEventRepository.deleteEvent() - } catch (e: Exception) { - logger.error(e) - } - - delegate.onSessionApproved(approvedSession.toModal()) - } - - override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) { - try { - connectionEventRepository.deleteEvent() - } catch (e: Exception) { - logger.error(e) - } - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = rejectedSession.reason))) - delegate.onSessionRejected(rejectedSession.toModal()) - } - - override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) { - delegate.onSessionUpdate(updatedSession.toModal()) - } - - override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) { - delegate.onSessionEvent(sessionEvent.toModal()) - } - - override fun onSessionEvent(sessionEvent: Sign.Model.Event) { - delegate.onSessionEvent(sessionEvent.toModal()) - } - - override fun onSessionExtend(session: Sign.Model.Session) { - delegate.onSessionExtend(session.toModal()) - } - - override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { - clearSession() - delegate.onSessionDelete(deletedSession.toModal()) - } - - override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) { - if (response.result.id == siweRequestIdWithMessage?.first) { - if (response.result is Sign.Model.JsonRpcResponse.JsonRpcResult) { - val siweResponse = Modal.Model.SIWEAuthenticateResponse.Result( - id = response.result.id, - message = siweRequestIdWithMessage!!.second, - signature = (response.result as Sign.Model.JsonRpcResponse.JsonRpcResult).result - ) - siweRequestIdWithMessage = null - delegate.onSIWEAuthenticationResponse(siweResponse) - } else if (response.result is Sign.Model.JsonRpcResponse.JsonRpcError) { - val siweResponse = Modal.Model.SIWEAuthenticateResponse.Error( - id = response.result.id, - message = (response.result as Sign.Model.JsonRpcResponse.JsonRpcError).message, - code = (response.result as Sign.Model.JsonRpcResponse.JsonRpcError).code - ) - siweRequestIdWithMessage = null - delegate.onSIWEAuthenticationResponse(siweResponse) - } - } else { - delegate.onSessionRequestResponse(response.toModal()) - } - } - - override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) { - delegate.onSessionAuthenticateResponse(sessionAuthenticateResponse.toModal()) - } - - override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { - delegate.onProposalExpired(proposal.toModal()) - } - - override fun onRequestExpired(request: Sign.Model.ExpiredRequest) { - delegate.onRequestExpired(request.toModal()) - } - - override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { - delegate.onConnectionStateChange(state.toModal()) - } - - override fun onError(error: Sign.Model.Error) { - delegate.onError(error.toModal()) - } - } - SignClient.setDappDelegate(signDelegate) - } - - fun registerCoinbaseLauncher(activity: ComponentActivity) { - if (::coinbaseClient.isInitialized) { - coinbaseClient.setLauncher(activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> result.data?.data?.let { coinbaseClient.handleResponse(it) } }) - } - } - - fun unregisterCoinbase() { - if (::coinbaseClient.isInitialized) { - coinbaseClient.unregister() - } - } - - fun coinbaseIsEnabled() = ::coinbaseClient.isInitialized && coinbaseClient.isInstalled() && coinbaseClient.isLauncherSet() - - @Throws(IllegalStateException::class) - private fun checkEngineInitialization() { - check(::coinbaseClient.isInitialized) { - "Coinbase Client needs to be initialized first using the initialize function" - } - } - - internal class RedirectMissingThrowable(message: String) : Throwable(message) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/presets/Web3ModalChainsPresets.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/presets/Web3ModalChainsPresets.kt deleted file mode 100644 index cf21ffa44..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/presets/Web3ModalChainsPresets.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.walletconnect.web3.modal.presets - -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.utils.EthUtils - -object Web3ModalChainsPresets { - val ethToken = Modal.Model.Token(name = "Ether", symbol = "ETH", decimal = 18) - - val ethChains: Map = mapOf( - "1" to Modal.Model.Chain( - chainName = "Ethereum", - chainNamespace = "eip155", - chainReference = "1", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://cloudflare-eth.com", - blockExplorerUrl = "https://etherscan.io" - ), - "42161" to Modal.Model.Chain( - chainName = "Arbitrum One", - chainNamespace = "eip155", - chainReference = "42161", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://arb1.arbitrum.io/rpc", - blockExplorerUrl = "https://arbiscan.io" - ), - "137" to Modal.Model.Chain( - chainName = "Polygon", - chainNamespace = "eip155", - chainReference = "137", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = Modal.Model.Token("MATIC", "MATIC", 18), - rpcUrl = "https://polygon-rpc.com", - blockExplorerUrl = "https://polygonscan.com" - ), - "43114" to Modal.Model.Chain( - chainName = "Avalanche", - chainNamespace = "eip155", - chainReference = "43114", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = Modal.Model.Token("Avalanche", "AVAX", 18), - rpcUrl = "https://api.avax.network/ext/bc/C/rpc", - blockExplorerUrl = "https://snowtrace.io" - ), - "56" to Modal.Model.Chain( - chainName = "BNB Smart Chain", - chainNamespace = "eip155", - chainReference = "56", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = Modal.Model.Token("BNB", "BNB", 18), - rpcUrl = "https://rpc.ankr.com/bsc", - blockExplorerUrl = "https://bscscan.com" - ), - "10" to Modal.Model.Chain( - chainName = "OP Mainnet", - chainNamespace = "eip155", - chainReference = "10", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://mainnet.optimism.io", - blockExplorerUrl = "https://explorer.optimism.io" - ), - "100" to Modal.Model.Chain( - chainName = "Gnosis", - chainNamespace = "eip155", - chainReference = "100", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = Modal.Model.Token("Gnosis", "xDAI", 18), - rpcUrl = "https://rpc.gnosischain.com", - blockExplorerUrl = "https://blockscout.com/xdai/mainnet" - ), - "324" to Modal.Model.Chain( - chainName = "zkSync Era", - chainNamespace = "eip155", - chainReference = "324", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://mainnet.era.zksync.io", - blockExplorerUrl = "https://explorer.zksync.io" - ), - "7777777" to Modal.Model.Chain( - chainName = "Zora", - chainNamespace = "eip155", - chainReference = "7777777", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://rpc.zora.energy", - blockExplorerUrl = "https://explorer.zora.energy" - ), - "8453" to Modal.Model.Chain( - chainName = "Base", - chainNamespace = "eip155", - chainReference = "8453", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://mainnet.base.org", - blockExplorerUrl = "https://basescan.org" - ), - "42220" to Modal.Model.Chain( - chainName = "Celo", - chainNamespace = "eip155", - chainReference = "42220", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = Modal.Model.Token("CELO", "CELO", 18), - rpcUrl = "https://forno.celo.org", - blockExplorerUrl = "https://explorer.celo.org/mainnet" - ), - "1313161554" to Modal.Model.Chain( - chainName = "Aurora", - chainNamespace = "eip155", - chainReference = "1313161554", - requiredMethods = EthUtils.ethRequiredMethods, - optionalMethods = EthUtils.ethOptionalMethods, - events = EthUtils.ethEvents, - token = ethToken, - rpcUrl = "https://mainnet.aurora.dev", - blockExplorerUrl = "https://aurorascan.dev" - ), - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3Modal.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3Modal.kt deleted file mode 100644 index 4ed334e6d..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3Modal.kt +++ /dev/null @@ -1,73 +0,0 @@ -@file:OptIn(ExperimentalMaterialNavigationApi::class) - -package com.walletconnect.web3.modal.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import androidx.compose.runtime.Composable -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavOptions -import androidx.navigation.NavType -import androidx.navigation.fragment.dialog -import androidx.navigation.navArgument -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.google.accompanist.navigation.material.bottomSheet -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.Web3ModalComponent -import com.walletconnect.web3.modal.ui.navigation.Route - -internal const val CHOOSE_NETWORK_KEY = "chooseNetwork" -private const val CHOOSE_NETWORK_ARG = "{chooseNetwork}" -private val web3ModalPath = Route.WEB3MODAL.path + "/" + CHOOSE_NETWORK_ARG - -fun NavGraphBuilder.web3Modal() { - dialog(web3ModalPath) { argument(CHOOSE_NETWORK_KEY) { type = NavType.BoolType } } -} - -@SuppressLint("RestrictedApi") -fun NavController.openWeb3Modal( - shouldOpenChooseNetwork: Boolean = false, - onError: (Throwable) -> Unit = {} -) { - when { - findDestination(R.id.web3ModalGraph) != null -> { - navigate(R.id.web3ModalGraph, args = Bundle().apply { - putBoolean(CHOOSE_NETWORK_KEY, shouldOpenChooseNetwork) - }, navOptions = buildWeb3ModalNavOptions()) - } - findDestination(web3ModalPath) != null -> { - navigate( - route = Route.WEB3MODAL.path + "/$shouldOpenChooseNetwork", - navOptions = buildWeb3ModalNavOptions() - ) - } - else -> onError(IllegalStateException("Invalid web3Modal path")) - } -} - -fun NavGraphBuilder.web3ModalGraph(navController: NavController) { - bottomSheet( - route = web3ModalPath, - arguments = listOf(navArgument(CHOOSE_NETWORK_KEY) { type = NavType.BoolType }) - ) { - val shouldOpenChooseNetwork = it.arguments?.getBoolean(CHOOSE_NETWORK_KEY) ?: false - Web3Modal( - navController = navController, - shouldOpenChooseNetwork = shouldOpenChooseNetwork - ) - } -} - -private fun buildWeb3ModalNavOptions() = NavOptions.Builder().setLaunchSingleTop(true).build() - -@Composable -internal fun Web3Modal( - navController: NavController, - shouldOpenChooseNetwork: Boolean -) { - Web3ModalComponent( - closeModal = navController::popBackStack, - shouldOpenChooseNetwork = shouldOpenChooseNetwork - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalEvents.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalEvents.kt deleted file mode 100644 index 6acfeeaf8..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalEvents.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.web3.modal.ui - -sealed class Web3ModalEvents { - object SessionApproved: Web3ModalEvents() - object SessionRejected: Web3ModalEvents() - object NoAction: Web3ModalEvents() - object InvalidState: Web3ModalEvents() -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalSheet.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalSheet.kt deleted file mode 100644 index 19d0335f5..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalSheet.kt +++ /dev/null @@ -1,205 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.web3.modal.ui - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import androidx.activity.ComponentDialog -import androidx.activity.OnBackPressedCallback -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.compose.ui.platform.rememberNestedScrollInteropConnection -import androidx.compose.ui.unit.dp -import androidx.core.content.res.getColorOrThrow -import androidx.core.content.res.use -import androidx.navigation.NavHostController -import com.google.accompanist.navigation.animation.rememberAnimatedNavController -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.walletconnect.modal.utils.theme.toComposeColor -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.Web3ModalComponent -import com.walletconnect.web3.modal.ui.theme.ColorPalette - -class Web3ModalSheet : BottomSheetDialogFragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - requireContext().setTheme(R.style.Web3ModalTheme_DialogTheme) - super.onCreate(savedInstanceState) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val shouldOpenChooseNetwork = arguments.getShouldOpenChooseNetworkArg() - - val mode = requireContext().getThemeMode() - val colors = requireContext().getColorMap() - - return ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - Web3ModalTheme( - mode = mode, - lightColors = colors.getLightModeColors(), - darkColors = colors.getDarkModeColors() - ) { - Web3ModalComposeView(shouldOpenChooseNetwork) - } - } - } - } - - @Composable - private fun Web3ModalComposeView( - shouldOpenChooseNetwork: Boolean - ) { - val navController = rememberAnimatedNavController() - dialog?.setupDialog(navController) - - Surface(shape = RoundedCornerShape(topStart = 36.dp, topEnd = 36.dp)) { - Web3ModalComponent( - modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), - navController = navController, - shouldOpenChooseNetwork = shouldOpenChooseNetwork, - closeModal = { this@Web3ModalSheet.dismiss() }) - } - } - - private fun Dialog.setupDialog(navController: NavHostController) { - (this as? ComponentDialog)?.onBackPressedDispatcher?.addCallback( - this@Web3ModalSheet, onBackPressedCallback(navController) - ) - findViewById(com.google.android.material.R.id.design_bottom_sheet)?.let { - val behavior = BottomSheetBehavior.from(it) - behavior.state = BottomSheetBehavior.STATE_EXPANDED - } - } - - private fun onBackPressedCallback(navController: NavHostController) = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - if (navController.popBackStack().not()) { - dismiss() - } - } - } -} - -@Composable -internal fun Map.getLightModeColors(): Web3ModalTheme.Colors { - val defaultColors = Web3ModalTheme.provideLightWeb3ModalColors() - val (foreground, background) = provideColorPallets(defaultColors) - return Web3ModalTheme.provideLightWeb3ModalColors( - accent100 = get(R.attr.modalAccent100) ?: defaultColors.accent100, - accent90 = get(R.attr.modalAccent90) ?: defaultColors.accent90, - accent80 = get(R.attr.modalAccent80) ?: defaultColors.accent80, - foreground = foreground, - background = background, - overlay = get(R.attr.modalGrayGlass) ?: defaultColors.grayGlass, - success = get(R.attr.modalSuccess) ?: defaultColors.success, - error = get(R.attr.modalError) ?: defaultColors.error - ) -} - -@Composable -internal fun Map.getDarkModeColors(): Web3ModalTheme.Colors { - val defaultColors = Web3ModalTheme.provideDarkWeb3ModalColor() - val (foreground, background) = provideColorPallets(defaultColors) - return Web3ModalTheme.provideDarkWeb3ModalColor( - accent100 = get(R.attr.modalAccent100) ?: defaultColors.accent100, - accent90 = get(R.attr.modalAccent90) ?: defaultColors.accent90, - accent80 = get(R.attr.modalAccent80) ?: defaultColors.accent80, - foreground = foreground, - background = background, - overlay = get(R.attr.modalGrayGlass) ?: defaultColors.grayGlass, - success = get(R.attr.modalSuccess) ?: defaultColors.success, - error = get(R.attr.modalError) ?: defaultColors.error - ) -} - -private fun Map.provideColorPallets(defaultColors: Web3ModalTheme.Colors): Pair { - val foreground = ColorPalette( - color100 = this[R.attr.modalForeground100] ?: defaultColors.foreground.color100, - color125 = this[R.attr.modalForeground125] ?: defaultColors.foreground.color125, - color150 = this[R.attr.modalForeground150] ?: defaultColors.foreground.color150, - color175 = this[R.attr.modalForeground175] ?: defaultColors.foreground.color175, - color200 = this[R.attr.modalForeground200] ?: defaultColors.foreground.color200, - color225 = this[R.attr.modalForeground225] ?: defaultColors.foreground.color225, - color250 = this[R.attr.modalForeground250] ?: defaultColors.foreground.color250, - color275 = this[R.attr.modalForeground275] ?: defaultColors.foreground.color275, - color300 = this[R.attr.modalForeground300] ?: defaultColors.foreground.color300, - ) - val background = ColorPalette( - color100 = this[R.attr.modalBackground100] ?: defaultColors.background.color100, - color125 = this[R.attr.modalBackground125] ?: defaultColors.background.color125, - color150 = this[R.attr.modalBackground150] ?: defaultColors.background.color150, - color175 = this[R.attr.modalBackground175] ?: defaultColors.background.color175, - color200 = this[R.attr.modalBackground200] ?: defaultColors.background.color200, - color225 = this[R.attr.modalBackground225] ?: defaultColors.background.color225, - color250 = this[R.attr.modalBackground250] ?: defaultColors.background.color250, - color275 = this[R.attr.modalBackground275] ?: defaultColors.background.color275, - color300 = this[R.attr.modalBackground300] ?: defaultColors.background.color300, - ) - return (foreground to background) -} - -private fun Int.toThemeMode(): Web3ModalTheme.Mode = when (this) { - 1 -> Web3ModalTheme.Mode.DARK - 2 -> Web3ModalTheme.Mode.LIGHT - else -> Web3ModalTheme.Mode.AUTO -} - -private val themeColorsAttributesMap = mapOf( - 0 to R.attr.modalAccent100, - 1 to R.attr.modalAccent90, - 2 to R.attr.modalAccent80, - 3 to R.attr.modalForeground100, - 4 to R.attr.modalForeground125, - 5 to R.attr.modalForeground150, - 6 to R.attr.modalForeground175, - 7 to R.attr.modalForeground200, - 8 to R.attr.modalForeground225, - 9 to R.attr.modalForeground250, - 10 to R.attr.modalForeground275, - 11 to R.attr.modalForeground300, - 12 to R.attr.modalBackground100, - 13 to R.attr.modalBackground125, - 14 to R.attr.modalBackground150, - 15 to R.attr.modalBackground175, - 16 to R.attr.modalBackground200, - 17 to R.attr.modalBackground225, - 18 to R.attr.modalBackground250, - 19 to R.attr.modalBackground275, - 20 to R.attr.modalBackground300, - 21 to R.attr.modalGrayGlass, - 22 to R.attr.modalSuccess, - 23 to R.attr.modalError, -) - -internal fun Context.getColorMap() = obtainStyledAttributes(themeColorsAttributesMap.values.toIntArray()).use { - themeColorsAttributesMap.keys.map { id -> - themeColorsAttributesMap[id]!! to try { - it.getColorOrThrow(id).toComposeColor() - } catch (e: Exception) { - null - } - } -}.toMap() - -internal fun Context.getThemeMode() = obtainStyledAttributes(intArrayOf(R.attr.modalMode)).use { it.getInt(0, 0) }.toThemeMode() - -private fun Bundle?.getShouldOpenChooseNetworkArg() = this?.getBoolean(CHOOSE_NETWORK_KEY) ?: false diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalState.kt deleted file mode 100644 index ee9a54330..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.web3.modal.ui - -internal sealed class Web3ModalState { - object Connect : Web3ModalState() - - object Loading : Web3ModalState() - - data class Error(val error: Throwable) : Web3ModalState() - - object AccountState : Web3ModalState() -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalTheme.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalTheme.kt deleted file mode 100644 index f1dd8a62f..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalTheme.kt +++ /dev/null @@ -1,129 +0,0 @@ -package com.walletconnect.web3.modal.ui - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.graphics.Color -import com.walletconnect.web3.modal.ui.theme.ColorPalette -import com.walletconnect.web3.modal.ui.theme.CustomComposition -import com.walletconnect.web3.modal.ui.theme.LocalCustomComposition -import com.walletconnect.web3.modal.ui.theme.defaultDarkWeb3ModalColors -import com.walletconnect.web3.modal.ui.theme.defaultLightWeb3ModalColors - -@Composable -fun Web3ModalTheme( - mode: Web3ModalTheme.Mode = Web3ModalTheme.Mode.AUTO, - lightColors: Web3ModalTheme.Colors = Web3ModalTheme.provideLightWeb3ModalColors(), - darkColors: Web3ModalTheme.Colors = Web3ModalTheme.provideDarkWeb3ModalColor(), - content: @Composable () -> Unit -) { - val customComposition = CustomComposition( - mode = mode, - lightColors = lightColors, - darkColors = darkColors, - ) - CompositionLocalProvider( - LocalCustomComposition provides customComposition - ) { - content() - } -} - -object Web3ModalTheme { - - fun provideLightWeb3ModalColors( - accent100: Color = defaultLightWeb3ModalColors.accent100, - accent90: Color = defaultLightWeb3ModalColors.accent90, - accent80: Color = defaultLightWeb3ModalColors.accent80, - foreground: ColorPalette = defaultLightWeb3ModalColors.foreground, - background: ColorPalette = defaultLightWeb3ModalColors.background, - overlay: Color = defaultLightWeb3ModalColors.grayGlass, - success: Color = defaultLightWeb3ModalColors.success, - error: Color = defaultLightWeb3ModalColors.error - ): Colors = CustomWeb3ModalColor(accent100, accent90, accent80, foreground, background, overlay, success, error) - - fun provideDarkWeb3ModalColor( - accent100: Color = defaultDarkWeb3ModalColors.accent100, - accent90: Color = defaultDarkWeb3ModalColors.accent90, - accent80: Color = defaultDarkWeb3ModalColors.accent80, - foreground: ColorPalette = defaultDarkWeb3ModalColors.foreground, - background: ColorPalette = defaultDarkWeb3ModalColors.background, - overlay: Color = defaultDarkWeb3ModalColors.grayGlass, - success: Color = defaultDarkWeb3ModalColors.success, - error: Color = defaultDarkWeb3ModalColors.error - ): Colors = CustomWeb3ModalColor(accent100, accent90, accent80, foreground, background, overlay, success, error) - - fun provideForegroundLightColorPalette( - color100: Color = defaultLightWeb3ModalColors.foreground.color100, - color125: Color = defaultLightWeb3ModalColors.foreground.color125, - color150: Color = defaultLightWeb3ModalColors.foreground.color150, - color175: Color = defaultLightWeb3ModalColors.foreground.color175, - color200: Color = defaultLightWeb3ModalColors.foreground.color200, - color225: Color = defaultLightWeb3ModalColors.foreground.color225, - color250: Color = defaultLightWeb3ModalColors.foreground.color250, - color275: Color = defaultLightWeb3ModalColors.foreground.color275, - color300: Color = defaultLightWeb3ModalColors.foreground.color300, - ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) - - fun provideForegroundDarkColorPalette( - color100: Color = defaultDarkWeb3ModalColors.foreground.color100, - color125: Color = defaultDarkWeb3ModalColors.foreground.color125, - color150: Color = defaultDarkWeb3ModalColors.foreground.color150, - color175: Color = defaultDarkWeb3ModalColors.foreground.color175, - color200: Color = defaultDarkWeb3ModalColors.foreground.color200, - color225: Color = defaultDarkWeb3ModalColors.foreground.color225, - color250: Color = defaultDarkWeb3ModalColors.foreground.color250, - color275: Color = defaultDarkWeb3ModalColors.foreground.color275, - color300: Color = defaultDarkWeb3ModalColors.foreground.color300, - ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) - - fun provideBackgroundLightColorPalette( - color100: Color = defaultLightWeb3ModalColors.background.color100, - color125: Color = defaultLightWeb3ModalColors.background.color125, - color150: Color = defaultLightWeb3ModalColors.background.color150, - color175: Color = defaultLightWeb3ModalColors.background.color175, - color200: Color = defaultLightWeb3ModalColors.background.color200, - color225: Color = defaultLightWeb3ModalColors.background.color225, - color250: Color = defaultLightWeb3ModalColors.background.color250, - color275: Color = defaultLightWeb3ModalColors.background.color275, - color300: Color = defaultLightWeb3ModalColors.background.color300, - ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) - - fun provideBackgroundDarkColorPalette( - color100: Color = defaultDarkWeb3ModalColors.background.color100, - color125: Color = defaultDarkWeb3ModalColors.background.color125, - color150: Color = defaultDarkWeb3ModalColors.background.color150, - color175: Color = defaultDarkWeb3ModalColors.background.color175, - color200: Color = defaultDarkWeb3ModalColors.background.color200, - color225: Color = defaultDarkWeb3ModalColors.background.color225, - color250: Color = defaultDarkWeb3ModalColors.background.color250, - color275: Color = defaultDarkWeb3ModalColors.background.color275, - color300: Color = defaultDarkWeb3ModalColors.background.color300, - ) = ColorPalette(color100, color125, color150, color175, color200, color225, color250, color275, color300) - - - enum class Mode { - LIGHT, DARK, AUTO - } - - interface Colors { - val accent100: Color - val accent90: Color - val accent80: Color - val foreground: ColorPalette - val background: ColorPalette - val grayGlass: Color - val success: Color - val error: Color - } -} - -private class CustomWeb3ModalColor( - override val accent100: Color, - override val accent90: Color, - override val accent80: Color, - override val foreground: ColorPalette, - override val background: ColorPalette, - override val grayGlass: Color, - override val success: Color, - override val error: Color -) : Web3ModalTheme.Colors diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalView.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalView.kt deleted file mode 100644 index 2afd387e4..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalView.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.walletconnect.web3.modal.ui - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.FrameLayout -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.Web3ModalComponent - -class Web3ModalView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr) { - - private var shouldOpenNetwork: Boolean - private var closeModal: () -> Unit = {} - - fun setOnCloseModal(onCloseModal: () -> Unit) { - closeModal = onCloseModal - } - - init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Web3ModalView, 0, 0) - shouldOpenNetwork = typedArray.getBoolean(R.styleable.Web3ModalView_open_network_select, false) - typedArray.recycle() - - context.setTheme(R.style.Web3ModalTheme) - - val mode = context.getThemeMode() - val colors = context.getColorMap() - - LayoutInflater.from(context) - .inflate(R.layout.view_web3modal, this, true) - .findViewById(R.id.root) - .apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - Web3ModalTheme( - mode = mode, - lightColors = colors.getLightModeColors(), - darkColors = colors.getDarkModeColors() - ) { - Web3ModalComponent( - shouldOpenChooseNetwork = shouldOpenNetwork, - closeModal = closeModal - ) - } - } - } - } - -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalViewModel.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalViewModel.kt deleted file mode 100644 index 08a9f47aa..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/Web3ModalViewModel.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.walletconnect.web3.modal.ui - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch - -internal class Web3ModalViewModel : ViewModel() { - - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() - private val _modalState: MutableStateFlow = MutableStateFlow(Web3ModalState.Loading) - val shouldDisconnect get() = web3ModalEngine.shouldDisconnect - - val modalState: StateFlow - get() = _modalState.asStateFlow() - - init { - require(Web3Modal.chains.isNotEmpty()) { "Be sure to set the Chains using Web3Modal.setChains" } - initModalState() - } - - fun disconnect() { - web3ModalEngine.disconnect( - onSuccess = { println("Disconnected successfully") }, - onError = { println("Disconnect error: $it") } - ) - } - - internal fun initModalState() { - viewModelScope.launch { - web3ModalEngine.getActiveSession()?.let { _ -> - createAccountModalState() - } ?: createConnectModalState() - } - } - - private fun createAccountModalState() { - _modalState.value = Web3ModalState.AccountState - } - - private fun createConnectModalState() { - _modalState.value = Web3ModalState.Connect - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/AccountButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/AccountButton.kt deleted file mode 100644 index ef4ad0f17..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/AccountButton.kt +++ /dev/null @@ -1,215 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.TransparentSurface -import com.walletconnect.web3.modal.ui.components.internal.commons.account.generateAvatarColors -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.components.internal.commons.button.TextButton -import com.walletconnect.web3.modal.ui.components.internal.commons.network.CircleNetworkImage -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.getImageData -import com.walletconnect.web3.modal.utils.toVisibleAddress - -enum class AccountButtonType { - NORMAL, MIXED -} - -internal sealed class AccountButtonState { - object Loading : AccountButtonState() - - data class Normal(val address: String) : AccountButtonState() - - data class Mixed( - val address: String, - val chainImage: Modal.Model.ChainImage, - val chainName: String, - val balance: String? - ) : AccountButtonState() - - object Invalid : AccountButtonState() -} - -@Composable -fun AccountButton( - state: Web3ModalState, - accountButtonType: AccountButtonType = AccountButtonType.NORMAL -) { - val accountState by when (accountButtonType) { - AccountButtonType.NORMAL -> state.accountNormalButtonState.collectAsState() - AccountButtonType.MIXED -> state.accountMixedButtonState.collectAsState() - } - AccountButtonState( - state = accountState, - onClick = state::openWeb3Modal - ) -} - -@Composable -internal fun AccountButtonState( - state: AccountButtonState, - onClick: () -> Unit, -) { - when (state) { - AccountButtonState.Invalid -> UnavailableSession() - AccountButtonState.Loading -> LoadingButton() - is AccountButtonState.Normal -> AccountButtonNormal(address = state.address, onClick = onClick) - is AccountButtonState.Mixed -> AccountButtonMixed( - address = state.address, - chainImage = state.chainImage, - chainData = state.balance ?: state.chainName, - onClick = onClick - ) - } -} - -@Composable -private fun AccountButtonMixed( - address: String, - chainImage: Modal.Model.ChainImage, - chainData: String, - onClick: () -> Unit, - isEnabled: Boolean = true -) { - ProvideWeb3ModalThemeComposition { - val backgroundColor: Color - val borderColor: Color - val textColor: Color - - if (isEnabled) { - backgroundColor = Web3ModalTheme.colors.grayGlass02 - borderColor = Web3ModalTheme.colors.grayGlass05 - textColor = Web3ModalTheme.colors.foreground.color100 - } else { - backgroundColor = Web3ModalTheme.colors.grayGlass15 - borderColor = Web3ModalTheme.colors.grayGlass05 - textColor = Web3ModalTheme.colors.grayGlass15 - } - - TransparentSurface(shape = RoundedCornerShape(100)) { - Box( - modifier = Modifier - .clickable(isEnabled) { onClick() } - .border(width = 1.dp, color = borderColor, shape = CircleShape) - .height(40.dp) - .background(backgroundColor) - ) { - Row( - modifier = Modifier.padding(start = 8.dp, end = 4.dp).fillMaxHeight(), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - CircleNetworkImage(data = chainImage.getImageData(), size = 24.dp, isEnabled = isEnabled) - HorizontalSpacer(width = 4.dp) - Text(text = chainData, style = Web3ModalTheme.typo.paragraph600.copy(color = textColor)) - HorizontalSpacer(width = 8.dp) - ImageButton( - text = address.toVisibleAddress(), image = { - Box( - modifier = Modifier - .size(22.dp) - .border(width = 2.dp, color = Web3ModalTheme.colors.grayGlass05, shape = CircleShape) - .padding(2.dp) - .background(brush = Brush.linearGradient(generateAvatarColors(address)), shape = CircleShape) - ) - }, - paddingValues = PaddingValues(start = 4.dp, end = 8.dp), - isEnabled = isEnabled, - style = ButtonStyle.ACCOUNT, - size = ButtonSize.ACCOUNT_S, - onClick = onClick - ) - } - } - } - } -} - -@Composable -private fun AccountButtonNormal( - address: String, - onClick: () -> Unit, - isEnabled: Boolean = true -) { - ProvideWeb3ModalThemeComposition { - ImageButton( - text = address.toVisibleAddress(), image = { - Box( - modifier = Modifier - .size(22.dp) - .border(width = 2.dp, color = Web3ModalTheme.colors.grayGlass05, shape = CircleShape) - .padding(2.dp) - .background(brush = Brush.linearGradient(generateAvatarColors(address)), shape = CircleShape) - ) - }, - paddingValues = PaddingValues(start = 6.dp, end = 12.dp, top = 4.dp, bottom = 4.dp), - isEnabled = isEnabled, - style = ButtonStyle.ACCOUNT, - size = ButtonSize.ACCOUNT_S, - onClick = onClick - ) - } -} - - -@Composable -private fun UnavailableSession() { - ProvideWeb3ModalThemeComposition { - TextButton(text = "Session Unavailable", style = ButtonStyle.ACCOUNT, size = ButtonSize.M, isEnabled = false, onClick = {}) - } -} - -@UiModePreview -@Composable -private fun UnavailableSessionPreview() { - ComponentPreview { - UnavailableSession() - } -} - -@UiModePreview -@Composable -private fun AccountButtonNormalPreview() { - MultipleComponentsPreview( - { AccountButtonNormal(address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}) }, - { AccountButtonNormal(address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}, isEnabled = false) } - ) -} - -@UiModePreview -@Composable -private fun AccountButtonMixedPreview() { - MultipleComponentsPreview( - { AccountButtonMixed(chainData = "ETH", chainImage = Modal.Model.ChainImage.Asset(R.drawable.ic_select_network), address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}) }, - { AccountButtonMixed(chainData = "ETH", chainImage = Modal.Model.ChainImage.Asset(R.drawable.ic_select_network), address = "0x59eAF7DD5a2f5e433083D8BbC8de3439542579cb", onClick = {}, isEnabled = false) } - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/ConnectButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/ConnectButton.kt deleted file mode 100644 index 5e13f1a2c..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/ConnectButton.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.components.internal.commons.button.TextButton -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition - -enum class ConnectButtonSize { - NORMAL, SMALL -} - -@Composable -fun ConnectButton( - state: Web3ModalState, - buttonSize: ConnectButtonSize = ConnectButtonSize.NORMAL -) { - val isLoading: Boolean by state.isOpen.collectAsState(initial = false) - val isConnected: Boolean by state.isConnected.collectAsState(initial = false) - - ConnectButton( - size = buttonSize, - isLoading = isLoading, - isEnabled = !isConnected - ) { - state.openWeb3Modal() - } -} - -@Composable -internal fun ConnectButton( - size: ConnectButtonSize, - isLoading: Boolean = false, - isEnabled: Boolean = true, - onClick: () -> Unit, -) { - ProvideWeb3ModalThemeComposition { - val buttonSize = when (size) { - ConnectButtonSize.NORMAL -> ButtonSize.M - ConnectButtonSize.SMALL -> ButtonSize.S - } - if (isLoading && isEnabled) { - ImageButton( - text = "Connecting...", - image = { LoadingSpinner(size = 10.dp, strokeWidth = 2.dp) }, - style = ButtonStyle.LOADING, - size = buttonSize - ) {} - } else { - val text = when (size) { - ConnectButtonSize.NORMAL -> "Connect wallet" - ConnectButtonSize.SMALL -> "Connect" - } - TextButton( - text = text, - style = ButtonStyle.MAIN, - size = buttonSize, - isEnabled = isEnabled, - onClick = onClick - ) - } - } -} - -@UiModePreview -@Composable -private fun ConnectButtonPreview() { - MultipleComponentsPreview( - { ConnectButton(size = ConnectButtonSize.NORMAL) {} }, - { ConnectButton(size = ConnectButtonSize.SMALL) {} }, - ) -} - -@UiModePreview -@Composable -private fun DisabledConnectButtonPreview() { - MultipleComponentsPreview( - { ConnectButton(size = ConnectButtonSize.NORMAL, isEnabled = false) {} }, - { ConnectButton(size = ConnectButtonSize.SMALL, isEnabled = false) {} }, - ) -} - -@UiModePreview -@Composable -private fun LoadingConnectButtonPreview() { - MultipleComponentsPreview( - { ConnectButton(size = ConnectButtonSize.NORMAL, isLoading = true) {} }, - { ConnectButton(size = ConnectButtonSize.SMALL, isLoading = true) {} }, - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/LoadingButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/LoadingButton.kt deleted file mode 100644 index 607a26f37..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/LoadingButton.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.width -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.StyledButton -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition - -@Composable -internal fun LoadingButton() { - ProvideWeb3ModalThemeComposition { - StyledButton(style = ButtonStyle.ACCOUNT, size = ButtonSize.M, onClick = {}) { - Box( - modifier = Modifier.width(100.dp), contentAlignment = Alignment.Center - ) { - LoadingSpinner(size = 16.dp, strokeWidth = 1.dp) - } - } - } -} - -@UiModePreview -@Composable -private fun LoadingButtonPreview() { - ComponentPreview { - LoadingButton() - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/NetworkButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/NetworkButton.kt deleted file mode 100644 index 538b777cc..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/NetworkButton.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.SelectNetworkIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.components.internal.commons.network.CircleNetworkImage -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import com.walletconnect.web3.modal.utils.getImageData - -@Composable -fun NetworkButton( - state: Web3ModalState -) { - val selectedChain by state.selectedChain.collectAsState(initial = null) - val image: @Composable () -> Unit = selectedChain?.let { chain -> - { CircleNetworkImage(data = chain.getImageData(), size = 24.dp) } - } ?: { - SelectNetworkIcon() - } - NetworkButton( - text = selectedChain?.chainName ?: "Select Network", - image = image, - isEnabled = true, - onClick = { state.openWeb3Modal(true, selectedChain != null) } - ) -} - -@Composable -internal fun NetworkButton( - text: String, - image: @Composable () -> Unit, - onClick: () -> Unit, - isEnabled: Boolean = true -) { - ProvideWeb3ModalThemeComposition { - ImageButton( - text = text, - image = { image() }, - isEnabled = isEnabled, - style = ButtonStyle.ACCOUNT, - size = ButtonSize.ACCOUNT_M, - onClick = onClick - ) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3Button.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3Button.kt deleted file mode 100644 index 34c1f27ff..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3Button.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.animation.AnimatedContent -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue - -@Composable -fun Web3Button( - state: Web3ModalState, - accountButtonType: AccountButtonType = AccountButtonType.NORMAL, - connectButtonSize: ConnectButtonSize = ConnectButtonSize.NORMAL -) { - val isConnected by state.isConnected.collectAsState(initial = false) - - AnimatedContent( - targetState = isConnected, - label = "Web3ButtonState" - ) { isConnected -> - if (isConnected) { - AccountButton(state = state, accountButtonType = accountButtonType) - } else { - ConnectButton(state = state, buttonSize = connectButtonSize) - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3ModalState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3ModalState.kt deleted file mode 100644 index ed5f71a36..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/Web3ModalState.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.GetEthBalanceUseCase -import com.walletconnect.web3.modal.domain.usecase.GetSessionUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSessionUseCase -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import com.walletconnect.web3.modal.ui.components.ComponentDelegate -import com.walletconnect.web3.modal.ui.components.ComponentEvent -import com.walletconnect.web3.modal.ui.openWeb3Modal -import com.walletconnect.web3.modal.utils.getChainNetworkImageUrl -import com.walletconnect.web3.modal.utils.getChains -import com.walletconnect.web3.modal.utils.getSelectedChain -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -@Composable -fun rememberWeb3ModalState( - coroutineScope: CoroutineScope = rememberCoroutineScope(), - navController: NavController -): Web3ModalState { - return remember(navController) { - Web3ModalState(coroutineScope, navController) - } -} - -class Web3ModalState( - coroutineScope: CoroutineScope, - private val navController: NavController -) { - private val logger: Logger = wcKoinApp.koin.get() - private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() - private val observeSessionTopicUseCase: ObserveSessionUseCase = wcKoinApp.koin.get() - private val getSessionUseCase: GetSessionUseCase = wcKoinApp.koin.get() - private val getEthBalanceUseCase: GetEthBalanceUseCase = wcKoinApp.koin.get() - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() - private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() - private val sessionTopicFlow = observeSessionTopicUseCase() - - val isOpen = ComponentDelegate.modalComponentEvent - .map { event -> - sendModalCloseOrOpenEvents(event) - event.isOpen - } - .stateIn(coroutineScope, started = SharingStarted.Lazily, ComponentDelegate.isModalOpen) - - val isConnected = sessionTopicFlow - .map { it != null && getSessionUseCase() != null } - .map { Web3Modal.getAccount() != null } - .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = false) - - internal val selectedChain = observeSelectedChainUseCase().map { savedChainId -> - Web3Modal.chains.find { it.id == savedChainId } - } - - internal val accountNormalButtonState = sessionTopicFlow.combine(selectedChain) { session, chain -> session to chain } - .mapOrAccountState(AccountButtonType.NORMAL) - .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = AccountButtonState.Loading) - - internal val accountMixedButtonState = sessionTopicFlow.combine(selectedChain) { session, chain -> session to chain } - .mapOrAccountState(AccountButtonType.MIXED) - .stateIn(coroutineScope, started = SharingStarted.Lazily, initialValue = AccountButtonState.Loading) - - private fun Flow>.mapOrAccountState(accountButtonType: AccountButtonType) = - map { web3ModalEngine.getActiveSession()?.mapToAccountButtonState(accountButtonType) ?: AccountButtonState.Invalid } - - private fun sendModalCloseOrOpenEvents(event: ComponentEvent) { - when { - event.isOpen && isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_OPEN, Properties(connected = true))) - event.isOpen && !isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_OPEN, Properties(connected = false))) - !event.isOpen && isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_CLOSE, Properties(connected = true))) - !event.isOpen && !isConnected.value -> sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.MODAL_CLOSE, Properties(connected = false))) - } - } - - private suspend fun Session.mapToAccountButtonState(accountButtonType: AccountButtonType) = try { - val chains = getChains() - val selectedChain = chains.getSelectedChain(this.chain) - val address = this.address - when (accountButtonType) { - AccountButtonType.NORMAL -> AccountButtonState.Normal(address = address) - AccountButtonType.MIXED -> { - val balance = getBalance(selectedChain, address) - AccountButtonState.Mixed( - address = address, - chainImage = selectedChain.chainImage ?: getChainNetworkImageUrl(selectedChain.chainReference), - chainName = selectedChain.chainName, - balance = balance - ) - } - } - } catch (e: Exception) { - AccountButtonState.Invalid - } - - private suspend fun getBalance(selectedChain: Modal.Model.Chain, address: String) = - selectedChain.rpcUrl?.let { url -> getEthBalanceUseCase(selectedChain.token, url, address)?.valueWithSymbol } - - internal fun openWeb3Modal(shouldOpenChooseNetwork: Boolean = false, isActiveNetwork: Boolean = false) { - if (shouldOpenChooseNetwork && isActiveNetwork) { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORKS)) - } - - navController.openWeb3Modal( - shouldOpenChooseNetwork = shouldOpenChooseNetwork, - onError = { logger.error(it) } - ) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/AccountButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/AccountButton.kt deleted file mode 100644 index 65fb44038..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/AccountButton.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.FrameLayout -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.navigation.findNavController -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.button.AccountButton -import com.walletconnect.web3.modal.ui.components.button.rememberWeb3ModalState -import com.walletconnect.web3.modal.utils.toAccountButtonType - -class AccountButton @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr) { - - init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AccountButton, 0, 0) - val accountButtonType = typedArray.getInteger(R.styleable.AccountButton_account_button_type, 0).toAccountButtonType() - typedArray.recycle() - - LayoutInflater.from(context) - .inflate(R.layout.view_button, this, true) - .findViewById(R.id.root) - .apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - val web3ModalState = rememberWeb3ModalState(navController = findNavController()) - AccountButton( - state = web3ModalState, - accountButtonType = accountButtonType - ) - } - } - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/ConnectButton.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/ConnectButton.kt deleted file mode 100644 index eb00689ef..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/ConnectButton.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.FrameLayout -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.navigation.findNavController -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.button.ConnectButton -import com.walletconnect.web3.modal.ui.components.button.rememberWeb3ModalState -import com.walletconnect.web3.modal.utils.toConnectButtonSize - -class ConnectButton @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr) { - - init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ConnectButton, 0, 0) - val connectButtonSize = typedArray.getInteger(R.styleable.ConnectButton_connect_button_size, 0).toConnectButtonSize() - typedArray.recycle() - - LayoutInflater.from(context) - .inflate(R.layout.view_button, this, true) - .findViewById(R.id.root) - .apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - val web3ModalState = rememberWeb3ModalState(navController = findNavController()) - ConnectButton(state = web3ModalState, buttonSize = connectButtonSize) - } - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/Web3Button.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/Web3Button.kt deleted file mode 100644 index 734c7b306..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/button/views/Web3Button.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.button.views - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.FrameLayout -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.navigation.findNavController -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.button.Web3Button -import com.walletconnect.web3.modal.ui.components.button.rememberWeb3ModalState -import com.walletconnect.web3.modal.utils.toAccountButtonType -import com.walletconnect.web3.modal.utils.toConnectButtonSize - -class Web3Button @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr) { - - init { - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.Web3Button, 0, 0) - val connectButtonSize = typedArray.getInteger(R.styleable.Web3Button_connect_button_size, 0).toConnectButtonSize() - val accountButtonType = typedArray.getInteger(R.styleable.Web3Button_account_button_type, 0).toAccountButtonType() - typedArray.recycle() - - LayoutInflater.from(context) - .inflate(R.layout.view_button, this, true) - .findViewById(R.id.root) - .apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - val web3ModalState = rememberWeb3ModalState(navController = findNavController()) - Web3Button( - state = web3ModalState, - accountButtonType = accountButtonType, - connectButtonSize = connectButtonSize - ) - } - } - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalComponent.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalComponent.kt deleted file mode 100644 index 5b2bcdd4b..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalComponent.kt +++ /dev/null @@ -1,127 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.web3.modal.ui.components.internal - -import android.annotation.SuppressLint -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.togetherWith -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import com.google.accompanist.navigation.animation.rememberAnimatedNavController -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.ui.Web3ModalState -import com.walletconnect.web3.modal.ui.Web3ModalViewModel -import com.walletconnect.web3.modal.ui.components.internal.root.Web3ModalRoot -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.account.AccountNavGraph -import com.walletconnect.web3.modal.ui.routes.connect.ConnectionNavGraph -import com.walletconnect.web3.modal.ui.utils.ComposableLifecycleEffect -import com.walletconnect.web3.modal.ui.utils.toComponentEvent -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -@Composable -fun Web3ModalComponent( - shouldOpenChooseNetwork: Boolean, - closeModal: () -> Unit -) { - Web3ModalComponent( - navController = rememberAnimatedNavController(), - shouldOpenChooseNetwork = shouldOpenChooseNetwork, - closeModal = closeModal - ) -} - -@SuppressLint("RestrictedApi") -@Composable -internal fun Web3ModalComponent( - modifier: Modifier = Modifier, - navController: NavHostController = rememberAnimatedNavController(), - shouldOpenChooseNetwork: Boolean, - closeModal: () -> Unit -) { - val web3ModalViewModel: Web3ModalViewModel = viewModel() - val state by web3ModalViewModel.modalState.collectAsState() - val coroutineScope = rememberCoroutineScope() - - LaunchedEffect(Unit) { - Web3ModalDelegate - .wcEventModels - .onEach { event -> - when (event) { - is Modal.Model.SIWEAuthenticateResponse.Result, is Modal.Model.SessionAuthenticateResponse.Result -> closeModal() - is Modal.Model.ApprovedSession -> { - if (Web3Modal.authPayloadParams != null) { - navController.navigate(Route.SIWE_FALLBACK.path) - } else { - closeModal() - } - } - is Modal.Model.DeletedSession.Success -> closeModal() - - else -> Unit - } - } - .collect() - } - - ComposableLifecycleEffect( - onEvent = { _, event -> - coroutineScope.launch { - event.toComponentEvent(onClosed = { - if (navController.currentDestination?.route == Route.SIWE_FALLBACK.path && web3ModalViewModel.shouldDisconnect) { - web3ModalViewModel.disconnect() - } - }) - } - } - ) - - Web3ModalRoot( - modifier = modifier, - navController = navController, - closeModal = closeModal - ) { - AnimatedContent( - targetState = state, - contentAlignment = Alignment.BottomCenter, - transitionSpec = { - (fadeIn() + slideInVertically(animationSpec = tween(500), - initialOffsetY = { fullHeight -> fullHeight })).togetherWith(fadeOut(animationSpec = tween(500))) - }, - label = "Root Animated content" - ) { state -> - when (state) { - is Web3ModalState.Connect -> ConnectionNavGraph( - navController = navController, - closeModal = closeModal, - shouldOpenChooseNetwork = shouldOpenChooseNetwork - ) - - is Web3ModalState.AccountState -> AccountNavGraph( - navController = navController, - closeModal = closeModal, - shouldOpenChangeNetwork = shouldOpenChooseNetwork - ) - - Web3ModalState.Loading -> LoadingModalState() - is Web3ModalState.Error -> ErrorModalState(retry = web3ModalViewModel::initModalState) - } - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalStates.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalStates.kt deleted file mode 100644 index 5195ee3ff..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalStates.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.walletconnect.modal.ui.components.common.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.TryAgainButton -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun LoadingModalState() { - Box( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - contentAlignment = Alignment.Center, - ) { - LoadingSpinner() - } -} - -@Composable -internal fun ErrorModalState(retry: () -> Unit) { - Column( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = "Something went wrong", style = Web3ModalTheme.typo.paragraph400) - VerticalSpacer(height = 10.dp) - TryAgainButton( - size = ButtonSize.M, - style = ButtonStyle.MAIN - ) { - retry() - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBar.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBar.kt deleted file mode 100644 index dec5a462a..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/Web3ModalTopBar.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal - -import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.ui.components.internal.commons.BackArrowIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.CloseIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.QuestionMarkIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.TestTags -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview - -@Composable -internal fun Web3ModalTopBar( - title: String, - startIcon: @Composable () -> Unit, - onCloseIconClick: () -> Unit -) { - Row( - modifier = Modifier - .height(64.dp) - .fillMaxWidth() - .padding(horizontal = 20.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Box(modifier = Modifier.size(40.dp)) { - startIcon() - } - Text( - text = title, - style = Web3ModalTheme.typo.paragraph600.copy( - color = Web3ModalTheme.colors.foreground.color100, - textAlign = TextAlign.Center - ), - modifier = Modifier.weight(1f).testTag(TestTags.TITLE) - ) - CloseIcon(onClick = onCloseIconClick) - } -} - -@Composable -@UiModePreview -private fun PreviewWeb3ModalTopBar() { - MultipleComponentsPreview( - { Web3ModalTopBar(title = "WalletConnect", startIcon = { BackArrowIcon {} }, {}) }, - { Web3ModalTopBar(title = "WalletConnect", startIcon = { QuestionMarkIcon() }, {}) } - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Dividers.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Dividers.kt deleted file mode 100644 index dd33922fe..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Dividers.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons - -import androidx.compose.material.Divider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun FullWidthDivider(modifier: Modifier = Modifier) { - Divider(color = Web3ModalTheme.colors.grayGlass05, modifier = modifier) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Label.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Label.kt deleted file mode 100644 index 9cc531e9e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Label.kt +++ /dev/null @@ -1,129 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun AllLabel(isEnabled: Boolean = true) { - ListLabel(text = "ALL", isEnabled = isEnabled) -} - -@Composable -internal fun TextLabel(text: String, isEnabled: Boolean = true) { - ListLabel( - text = text, - isEnabled = isEnabled, - backgroundColor = Web3ModalTheme.colors.grayGlass10, - labelTextColor = Web3ModalTheme.colors.foreground.color150 - ) -} - -@Composable -internal fun GetWalletLabel(isEnabled: Boolean = true) { - ListLabel(text = "GET WALLET", isEnabled = isEnabled) -} - -@Composable -internal fun RecentLabel(isEnabled: Boolean = true) { - ListLabel( - text = "RECENT", - isEnabled = isEnabled, - backgroundColor = Web3ModalTheme.colors.grayGlass10, - labelTextColor = Web3ModalTheme.colors.foreground.color150 - ) -} - -@Composable -internal fun InstalledLabel(isEnabled: Boolean = true) { - ListLabel( - text = "INSTALLED", - isEnabled = isEnabled, - backgroundColor = Web3ModalTheme.colors.success15, - labelTextColor = Web3ModalTheme.colors.success - ) -} - -@Composable -private fun ListLabel( - text: String, - isEnabled: Boolean, - backgroundColor: Color = Web3ModalTheme.colors.accent15, - labelTextColor: Color = Web3ModalTheme.colors.accent100 -) { - val textColor: Color - val background: Color - if (isEnabled) { - background = backgroundColor - textColor = labelTextColor - } else { - background = Web3ModalTheme.colors.grayGlass10 - textColor = Web3ModalTheme.colors.foreground.color300 - } - Box( - modifier = Modifier - .height(20.dp) - .background(background, shape = RoundedCornerShape(4.dp)) - .padding( horizontal = 5.dp), - contentAlignment = Alignment.Center - ) { - Text(text = text, style = Web3ModalTheme.typo.micro700.copy(textColor)) - } -} - -@Composable -@UiModePreview -private fun AllLabelPreview() { - MultipleComponentsPreview( - { AllLabel() }, - { AllLabel(false) }, - ) -} - -@Composable -@UiModePreview -private fun TextLabelPreview() { - MultipleComponentsPreview( - { TextLabel("240+") }, - { TextLabel("240+", false) }, - ) -} - -@Composable -@UiModePreview -private fun GetWalletLabelPreview() { - MultipleComponentsPreview( - { GetWalletLabel() }, - { GetWalletLabel(false) }, - ) -} - -@Composable -@UiModePreview -private fun RecentLabelPreview() { - MultipleComponentsPreview( - { RecentLabel() }, - { RecentLabel(false) }, - ) -} - -@Composable -@UiModePreview -private fun InstalledLabelPreview() { - MultipleComponentsPreview( - { InstalledLabel() }, - { InstalledLabel(false) }, - ) -} - diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Spacers.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Spacers.kt deleted file mode 100644 index 5a093755a..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Spacers.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons - -import androidx.compose.foundation.layout.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp - -@Composable -internal fun VerticalSpacer(height: Dp) { - Spacer(modifier = Modifier.height(height)) -} - -@Composable -internal fun HorizontalSpacer(width: Dp) { - Spacer(modifier = Modifier.width(width)) -} - -@Composable -internal fun RowScope.WeightSpacer(weight: Float = 1f) { - Spacer(modifier = Modifier.weight(weight)) -} - -@Composable -internal fun ColumnScope.WeightSpacer(weight: Float = 1f) { - Spacer(modifier = Modifier.weight(weight)) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/TestTags.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/TestTags.kt deleted file mode 100644 index 174291916..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/TestTags.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons - -internal object TestTags { - const val TITLE = "Title" -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Wallets.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Wallets.kt deleted file mode 100644 index 7fe3f2031..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/Wallets.kt +++ /dev/null @@ -1,193 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import coil.request.ImageRequest -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.grayColorFilter -import com.walletconnect.web3.modal.utils.imageHeaders - -@Composable -internal fun MultipleWalletIcon(wallets: List) { - Column( - modifier = Modifier - .size(40.dp) - .background(Web3ModalTheme.colors.background.color200, shape = RoundedCornerShape(10.dp)) - .padding(1.dp) - .border(1.dp, Web3ModalTheme.colors.grayGlass10, shape = RoundedCornerShape(10.dp)), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - wallets.chunked(2).forEach { - Row( - modifier = Modifier.width(40.dp), - horizontalArrangement = Arrangement.Center - ) { - it.forEach { item -> - WalletImage( - url = item.imageUrl, - modifier = Modifier - .size(16.dp) - .padding(1.dp) - .clip(RoundedCornerShape(4.dp)) - ) - } - } - } - - } -} - -internal fun LazyGridScope.walletsGridItems( - wallets: List, - onWalletItemClick: (Wallet) -> Unit -) { - itemsIndexed( - items = wallets, - key = { _, wallet -> wallet.id } - ) { _, wallet -> - WalletGridItem( - wallet = wallet, - onWalletItemClick = onWalletItemClick - ) - } -} - -@Composable -internal fun WalletImageWithLoader(url: String?) { - LoadingBorder( - cornerRadius = 28.dp - ) { - RoundedWalletImage(url = url) - } -} - -@Composable -internal fun RoundedWalletImage(url: String?) { - WalletImage( - url = url, modifier = Modifier - .size(80.dp) - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass10, shape = RoundedCornerShape(28.dp)) - .clip(RoundedCornerShape(28.dp)) - ) -} - -@Composable -internal fun WalletImage(url: String?, isEnabled: Boolean = true, modifier: Modifier) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(url) - .fallback(R.drawable.walletconnect_blue) - .crossfade(true) - .placeholder(R.drawable.wallet_placeholder) - .imageHeaders() - .build(), - contentDescription = null, - modifier = modifier, - colorFilter = if (isEnabled) null else grayColorFilter - ) -} - -@Composable -internal fun BoxScope.InstalledWalletIcon() { - Icon( - modifier = Modifier - .offset(x = 2.dp, y = 2.dp) - .background(Web3ModalTheme.colors.background.color125, shape = CircleShape) - .align(Alignment.BottomEnd) - .background(Web3ModalTheme.colors.grayGlass02, shape = CircleShape) - .padding(2.dp) - .size(12.dp) - .background(Web3ModalTheme.colors.success.copy(0.15f), shape = CircleShape) - .padding(2.dp), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_check), - contentDescription = "WalletConnectLogo", - tint = Web3ModalTheme.colors.success - ) -} - -@Composable -internal fun WalletGridItem( - wallet: Wallet, - onWalletItemClick: (Wallet) -> Unit -) { - TransparentSurface( - modifier = Modifier.padding(2.dp), - shape = RoundedCornerShape(16.dp) - ) { - Column( - modifier = Modifier - .width(76.dp) - .height(96.dp) - .background(Web3ModalTheme.colors.grayGlass02) - .clickable { onWalletItemClick(wallet) }, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Box { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .size(56.dp) - .clip(RoundedCornerShape(16.dp)) - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass10, shape = RoundedCornerShape(16.dp)) - ) - if (wallet.isWalletInstalled) { - InstalledWalletIcon() - } - } - VerticalSpacer(height = 8.dp) - Text( - text = wallet.name, - style = Web3ModalTheme.typo.tiny500, - textAlign = TextAlign.Center, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 2.dp) - ) - } - } -} - -@UiModePreview -@Composable -private fun PreviewWallets() { - MultipleComponentsPreview( - { WalletGridItem(wallet = testWallets.first(), onWalletItemClick = {}) }, - { WalletGridItem(wallet = testWallets[1], onWalletItemClick = {}) }, - { MultipleWalletIcon(wallets = testWallets.take(4)) }, - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountName.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountName.kt deleted file mode 100644 index 914b86d4f..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/account/AccountName.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.account - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.ClipboardManager -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.unit.dp -import com.walletconnect.modal.ui.components.common.roundedClickable -import com.walletconnect.web3.modal.domain.model.AccountData -import com.walletconnect.web3.modal.domain.model.Identity -import com.walletconnect.web3.modal.ui.components.internal.commons.CopyIcon -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.accountDataPreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.toVisibleAddress - -@Composable -internal fun AccountName(accountData: AccountData) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - val clipboardManager: ClipboardManager = LocalClipboardManager.current - val name = accountData.identity?.name ?: accountData.address.toVisibleAddress() - Text(text = name, style = Web3ModalTheme.typo.mediumTitle600) - CopyIcon( - modifier = Modifier - .padding(10.dp) - .size(16.dp) - .roundedClickable { clipboardManager.setText(AnnotatedString(accountData.address)) } - ) - } -} - -@UiModePreview -@Composable -private fun AccountAddressPreview() { - ComponentPreview { - AccountName(accountDataPreview) - } -} - -@UiModePreview -@Composable -private fun AccountNamePreview() { - ComponentPreview { - AccountName(accountDataPreview.copy(identity = Identity(name = "testIdentity.eth"))) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ButtonStyle.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ButtonStyle.kt deleted file mode 100644 index 8e64cb11d..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/button/ButtonStyle.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.button - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -internal data class ButtonData( - val size: ButtonSize, - val style: ButtonStyle, - val textStyle: TextStyle, - val tint: Color, - val background: Color -) - -internal enum class ButtonStyle { MAIN, ACCENT, SHADE, LOADING, ACCOUNT, LINK } - -internal enum class ButtonSize { M, S, ACCOUNT_M, ACCOUNT_S } - -@Composable -internal fun ButtonSize.getTextStyle() = when (this) { - ButtonSize.M, ButtonSize.ACCOUNT_M, ButtonSize.ACCOUNT_S -> Web3ModalTheme.typo.paragraph600 - ButtonSize.S -> Web3ModalTheme.typo.small600 -} - -@Composable -internal fun ButtonSize.getContentPadding() = when (this) { - ButtonSize.M -> PaddingValues(horizontal = 16.dp) - ButtonSize.S -> PaddingValues(horizontal = 12.dp) - ButtonSize.ACCOUNT_M -> PaddingValues(start = 8.dp, end = 12.dp) - ButtonSize.ACCOUNT_S -> PaddingValues(start = 6.dp, end = 12.dp) -} - -@Composable -internal fun ButtonSize.getHeight() = when (this) { - ButtonSize.M, ButtonSize.ACCOUNT_M -> 40.dp - ButtonSize.S, ButtonSize.ACCOUNT_S -> 32.dp -} - - -@Composable -internal fun ButtonStyle.getTextColor(isEnabled: Boolean) = when (this) { - ButtonStyle.MAIN -> if (isEnabled) Web3ModalTheme.colors.inverse100 else Web3ModalTheme.colors.foreground.color300 - ButtonStyle.ACCENT, ButtonStyle.LOADING -> if (isEnabled) Web3ModalTheme.colors.accent100 else Web3ModalTheme.colors.grayGlass20 - ButtonStyle.SHADE -> if (isEnabled) Web3ModalTheme.colors.foreground.color150 else Web3ModalTheme.colors.grayGlass15 - ButtonStyle.ACCOUNT -> if (isEnabled) Web3ModalTheme.colors.foreground.color100 else Web3ModalTheme.colors.grayGlass15 - ButtonStyle.LINK -> if(isEnabled) Web3ModalTheme.colors.foreground.color200 else Web3ModalTheme.colors.grayGlass15 -} - -@Composable -internal fun ButtonStyle.getBackgroundColor(isEnabled: Boolean) = when (this) { - ButtonStyle.MAIN -> if (isEnabled) Web3ModalTheme.colors.accent100 else Web3ModalTheme.colors.grayGlass20 - ButtonStyle.ACCENT -> if (isEnabled) Color.Transparent else Web3ModalTheme.colors.grayGlass10 - ButtonStyle.SHADE, ButtonStyle.LINK -> if (isEnabled) Color.Transparent else Web3ModalTheme.colors.grayGlass05 - ButtonStyle.LOADING -> Web3ModalTheme.colors.grayGlass10 - ButtonStyle.ACCOUNT -> if (isEnabled) Web3ModalTheme.colors.grayGlass10 else Web3ModalTheme.colors.grayGlass15 -} - -@Composable -internal fun ButtonStyle.getBorder(isEnabled: Boolean) = when (this) { - ButtonStyle.MAIN, ButtonStyle.LINK -> if (isEnabled) Color.Transparent else Web3ModalTheme.colors.grayGlass20 - ButtonStyle.ACCENT, ButtonStyle.SHADE, ButtonStyle.LOADING, ButtonStyle.ACCOUNT -> if (isEnabled) Web3ModalTheme.colors.grayGlass10 else Web3ModalTheme.colors.grayGlass05 -} - -internal data class ButtonPreview( - val style: ButtonStyle, - val size: ButtonSize, - val isEnabled: Boolean -) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/ActionEntry.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/ActionEntry.kt deleted file mode 100644 index 0ebd6fac7..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/ActionEntry.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.entry - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.CopyIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.LinkButton -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun ActionEntry( - text: String, - modifier: Modifier = Modifier, - icon: @Composable() ((Color) -> Unit)? = null, - isEnabled: Boolean = true, - contentPadding: PaddingValues = PaddingValues(0.dp), - onClick: () -> Unit -) { - BaseEntry( - isEnabled = isEnabled, - contentPadding = contentPadding - ) { colors -> - Row( - modifier = modifier - .clickable { onClick() } - .background(colors.background) - .padding(horizontal = 16.dp, vertical = 12.dp), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - icon?.let { - it(colors.textColor) - HorizontalSpacer(width = 8.dp) - } - Text(text = text, style = Web3ModalTheme.typo.paragraph500.copy(color = colors.textColor)) - } - } -} - -@Composable -internal fun CopyActionEntry(isEnabled: Boolean = true, onClick: () -> Unit) { - LinkButton( - text = "Copy link", - startIcon = { - CopyIcon(tint = it, modifier = Modifier.size(12.dp)) - }, - isEnabled = isEnabled, - size = ButtonSize.S, - onClick = onClick - ) -} - -@UiModePreview -@Composable -private fun ActionEntryPreview() { - MultipleComponentsPreview( - { ActionEntry(text = "Action without icon") {} }, - { ActionEntry(text = "Action without icon", isEnabled = false) {} }, - { ActionEntry(icon = { Image(imageVector = Icons.Default.Done, contentDescription = null, colorFilter = ColorFilter.tint(it)) }, text = "Action with icon") {} }, - { ActionEntry(isEnabled = false, icon = { Image(imageVector = Icons.Default.Done, contentDescription = null, colorFilter = ColorFilter.tint(it)) }, text = "Action with icon") {} }, - ) -} - -@UiModePreview -@Composable -private fun CopyActionPreview() { - MultipleComponentsPreview( - { CopyActionEntry {} }, - { CopyActionEntry(isEnabled = false) {} } - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/BaseEntry.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/BaseEntry.kt deleted file mode 100644 index 247fc0a39..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/BaseEntry.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.entry - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.TransparentSurface -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -internal data class EntryColors( - val background: Color, - val textColor: Color, - val secondaryColor: Color -) - -@Composable -internal fun BaseEntry( - isEnabled: Boolean, - contentPadding: PaddingValues = PaddingValues(0.dp), - content: @Composable (EntryColors) -> Unit -) { - val background: Color - val textColor: Color - val secondaryColor: Color - if (isEnabled) { - background = Web3ModalTheme.colors.grayGlass02 - textColor = Web3ModalTheme.colors.foreground.color100 - secondaryColor = Web3ModalTheme.colors.foreground.color200 - } else { - background = Web3ModalTheme.colors.grayGlass15 - textColor = Web3ModalTheme.colors.grayGlass15 - secondaryColor = Web3ModalTheme.colors.grayGlass15 - } - val entryColors = EntryColors(background, textColor, secondaryColor) - - TransparentSurface( - shape = RoundedCornerShape(16.dp), - modifier = Modifier.padding(contentPadding) - ) { - content(entryColors) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/StoreEntry.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/StoreEntry.kt deleted file mode 100644 index e8ddef096..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/entry/StoreEntry.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.entry - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.ForwardIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.TransparentSurface -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun StoreEntry( - text: String, - isEnabled: Boolean = true, - onClick: () -> Unit -) { - val background: Color - val textColor: Color - if (isEnabled) { - textColor = Web3ModalTheme.colors.foreground.color200 - background = Web3ModalTheme.colors.grayGlass02 - } else { - textColor = Web3ModalTheme.colors.foreground.color300 - background = Web3ModalTheme.colors.grayGlass10 - } - Surface( - color = Color.Transparent, - shape = RoundedCornerShape(16.dp), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(56.dp) - .background(background) - .padding(horizontal = 18.dp), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - Text(text = text, style = Web3ModalTheme.typo.paragraph500.copy(color = textColor), modifier = Modifier.weight(1f)) - HorizontalSpacer(width = 10.dp) - GetButton(onClick) - } - } -} - -@Composable -private fun GetButton(onClick: () -> Unit) { - TransparentSurface(shape = RoundedCornerShape(100)) { - Row( - modifier = Modifier - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass10, shape = CircleShape) - .clickable { onClick() } - .padding(start = 12.dp, end = 8.dp, top = 6.dp, bottom = 6.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text(text = "Get", style = Web3ModalTheme.typo.small500.copy(color = Web3ModalTheme.colors.accent100)) - HorizontalSpacer(width = 4.dp) - ForwardIcon(tint = Web3ModalTheme.colors.accent100) - } - } -} - -@UiModePreview -@Composable -private fun PreviewStoreEntry() { - val wallet = testWallets.first() - MultipleComponentsPreview( - { StoreEntry(text = "Don't have ${wallet.name}?") {} }, - { StoreEntry(text = "Don't have ${wallet.name}?", isEnabled = false) {} }, - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/BaseTextInput.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/BaseTextInput.kt deleted file mode 100644 index 1502f5ada..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/BaseTextInput.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.inputs - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun BaseTextInput( - inputState: InputState, - modifier: Modifier = Modifier, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - isEnabled: Boolean = true, - content: @Composable (innerTextField: @Composable () -> Unit, inputData: InputData) -> Unit -) { - val focusRequester = remember { FocusRequester() } - val borderColor: Color - val backgroundColor: Color - val state by inputState.state.collectAsState() - - when { - state.isFocused -> { - borderColor = Web3ModalTheme.colors.accent100 - backgroundColor = Web3ModalTheme.colors.grayGlass10 - } - - isEnabled -> { - borderColor = Web3ModalTheme.colors.grayGlass05 - backgroundColor = Web3ModalTheme.colors.grayGlass05 - } - - else -> { - borderColor = Web3ModalTheme.colors.grayGlass10 - backgroundColor = Web3ModalTheme.colors.grayGlass15 - } - } - - BasicTextField(value = state.text, - onValueChange = inputState::onTextChange, - textStyle = Web3ModalTheme.typo.paragraph400.copy(color = Web3ModalTheme.colors.foreground.color100), - cursorBrush = SolidColor(Web3ModalTheme.colors.accent100), - singleLine = true, - keyboardActions = KeyboardActions { inputState.submit(state.text) }, - keyboardOptions = keyboardOptions, - modifier = modifier - .background(color = backgroundColor, shape = RoundedCornerShape(12.dp)) - .border(width = 1.dp, color = borderColor, shape = RoundedCornerShape(12.dp)) - .onFocusChanged { inputState.onFocusChange(it.hasFocus) } - .focusRequester(focusRequester), - decorationBox = { - content(it, state) - }) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputCancel.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputCancel.kt deleted file mode 100644 index a4d315cd1..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/inputs/InputCancel.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.inputs - -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun InputCancel( - modifier: Modifier = Modifier, - isEnabled: Boolean = true, - onClick: () -> Unit -) { - val background: Color - val tint: Color - - if (isEnabled) { - tint = Web3ModalTheme.colors.background.color100 - background = Web3ModalTheme.colors.grayGlass20 - } else { - tint = Web3ModalTheme.colors.background.color100 - background = Web3ModalTheme.colors.grayGlass10 - } - - Surface( - color = background, - modifier = Modifier.size(18.dp).clickable { onClick() }.then(modifier), - shape = RoundedCornerShape(6.dp) - ) { - Image( - imageVector = ImageVector.vectorResource(R.drawable.ic_close), - contentDescription = ContentDescription.CLEAR.description, - modifier = Modifier.size(10.dp).padding(4.dp), - colorFilter = ColorFilter.tint(tint) - ) - } -} - -@UiModePreview -@Composable -private fun PreviewInputCancel() { - MultipleComponentsPreview( - { InputCancel() {} }, - { InputCancel(isEnabled = false) {} }, - ) -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkItem.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkItem.kt deleted file mode 100644 index 125383ebc..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/commons/network/ChainNetworkItem.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.commons.network - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.ui.components.internal.commons.TransparentSurface -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun ChainNetworkItem( - image: Modal.Model.ChainImage, - isSelected: Boolean, - networkName: String, - isEnabled: Boolean = true, - onItemClick: () -> Unit, -) { - val data = when (image) { - is Modal.Model.ChainImage.Asset -> image.id - is Modal.Model.ChainImage.Network -> image.url - } - val backgroundColor: Color - val textColor: Color - val borderColor: Color? - when { - isSelected -> { - backgroundColor = Web3ModalTheme.colors.accent10 - textColor = Web3ModalTheme.colors.accent100 - borderColor = Web3ModalTheme.colors.accent100 - } - - isEnabled -> { - backgroundColor = Web3ModalTheme.colors.grayGlass02 - textColor = Web3ModalTheme.colors.foreground.color100 - borderColor = null - } - - else -> { - backgroundColor = Web3ModalTheme.colors.grayGlass10 - textColor = Web3ModalTheme.colors.grayGlass15 - borderColor = null - } - } - TransparentSurface( - shape = RoundedCornerShape(16.dp), - modifier = Modifier.padding(2.dp) - ) { - Column( - modifier = Modifier - .width(76.dp) - .height(96.dp) - .background(backgroundColor) - .clickable(isEnabled) { onItemClick() }, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - HexagonNetworkImage( - data = data, - isEnabled = isEnabled, - borderColor = borderColor, - ) - VerticalSpacer(height = 8.dp) - Text( - text = networkName, - style = Web3ModalTheme.typo.tiny500.copy(textColor), - textAlign = TextAlign.Center, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 2.dp) - ) - } - } -} - -@UiModePreview -@Composable -private fun ChainNetworkItemPreview() { - val image = Modal.Model.ChainImage.Asset(R.drawable.system) - MultipleComponentsPreview( - { ChainNetworkItem(image = image, isSelected = true, isEnabled = true, networkName = "TestNetwork", onItemClick = {}) }, - { ChainNetworkItem(image = image, isSelected = false, isEnabled = true, networkName = "TestNetwork", onItemClick = {}) }, - { ChainNetworkItem(image = image, isSelected = false, isEnabled = false, networkName = "TestNetwork", onItemClick = {}) } - ) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/InputBox.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/InputBox.kt deleted file mode 100644 index 611791cbb..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/email/InputBox.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.email - -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.InputState -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun InputValidationBox( - inputState: InputState, - errorMessage: String, - errorAlign: TextAlign = TextAlign.Left, - content: @Composable () -> Unit, -) { - val hasError by inputState.hasError.collectAsState() - - Column(modifier = Modifier.animateContentSize()) { - content() - if (hasError) { - VerticalSpacer(height = 4.dp) - Text( - text = errorMessage, - modifier = Modifier - .padding(horizontal = 14.dp) - .fillMaxWidth(), - style = Web3ModalTheme.typo.tiny400.copy(color = Web3ModalTheme.colors.error, textAlign = errorAlign) - ) - } - } -} - diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRoot.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRoot.kt deleted file mode 100644 index 243c94b6c..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRoot.kt +++ /dev/null @@ -1,146 +0,0 @@ -@file:OptIn(ExperimentalComposeUiApi::class) - -package com.walletconnect.web3.modal.ui.components.internal.root - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import com.walletconnect.modal.ui.components.common.roundedClickable -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.ui.components.internal.Web3ModalTopBar -import com.walletconnect.web3.modal.ui.components.internal.commons.BackArrowIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.FullWidthDivider -import com.walletconnect.web3.modal.ui.components.internal.commons.QuestionMarkIcon -import com.walletconnect.web3.modal.ui.components.internal.snackbar.ModalSnackBarHost -import com.walletconnect.web3.modal.ui.components.internal.snackbar.SnackBarState -import com.walletconnect.web3.modal.ui.components.internal.snackbar.rememberSnackBarState -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.onEach - -@Composable -internal fun Web3ModalRoot( - navController: NavHostController, - modifier: Modifier = Modifier, - closeModal: () -> Unit, - content: @Composable () -> Unit -) { - val scope = rememberCoroutineScope() - val rootState = rememberWeb3ModalRootState(coroutineScope = scope, navController = navController) - val snackBarState = rememberSnackBarState(coroutineScope = scope) - val title by rootState.title.collectAsState(null) - - LaunchedEffect(Unit) { - Web3ModalDelegate - .wcEventModels - .filterIsInstance() - .onEach { event -> - snackBarState.showErrorSnack(event.throwable.localizedMessage ?: "Something went wrong") - } - .collect() - } - - Column( - verticalArrangement = Arrangement.Bottom, - modifier = modifier - ) { - ProvideWeb3ModalThemeComposition { - Web3ModalRoot(rootState, snackBarState, title, closeModal, content) - } - } -} - -@Composable -internal fun Web3ModalRoot( - rootState: Web3ModalRootState, - snackBarState: SnackBarState, - title: String?, - closeModal: () -> Unit, - content: @Composable () -> Unit -) { - ModalSnackBarHost(snackBarState) { - Column( - modifier = Modifier - .fillMaxWidth() - .background(Web3ModalTheme.colors.background.color125) - ) { - title?.let { title -> - Web3ModalTopBar( - title = title, - startIcon = { TopBarStartIcon(rootState) }, - onCloseIconClick = closeModal - ) - FullWidthDivider() - } - content() - } - } -} - -@Composable -private fun TopBarStartIcon( - rootState: Web3ModalRootState -) { - if (rootState.currentDestinationRoute == Route.SIWE_FALLBACK.path) { - questionMark(rootState) - } else { - if (rootState.canPopUp) { - val keyboardController = LocalSoftwareKeyboardController.current - BackArrowIcon(onClick = { - keyboardController?.hide() - rootState.popUp() - }) - } else { - when (rootState.currentDestinationRoute) { - Route.CONNECT_YOUR_WALLET.path -> questionMark(rootState) - } - } - } -} - -@Composable -private fun questionMark(rootState: Web3ModalRootState) { - QuestionMarkIcon( - modifier = Modifier - .size(36.dp) - .roundedClickable(onClick = rootState::navigateToHelp) - .padding(10.dp) - ) -} - -@Composable -@UiModePreview -private fun PreviewWeb3ModalRoot() { - val content: @Composable () -> Unit = { Box(modifier = Modifier.size(200.dp)) } - val scope = rememberCoroutineScope() - val navController = rememberNavController() - val rootState = rememberWeb3ModalRootState(coroutineScope = scope, navController = navController) - val snackBarState = rememberSnackBarState(coroutineScope = scope) - - MultipleComponentsPreview( - { Web3ModalRoot(rootState, snackBarState, null, {}, { content() }) }, - { Web3ModalRoot(rootState, snackBarState, "Top Bar Title", {}, { content() }) } - ) -} - diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootState.kt deleted file mode 100644 index d7d841ba5..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/root/Web3ModalRootState.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.root - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavController -import androidx.navigation.NavDestination -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.getTitleArg -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -@Composable -internal fun rememberWeb3ModalRootState( - coroutineScope: CoroutineScope, - navController: NavController -): Web3ModalRootState { - return remember(coroutineScope, navController) { - Web3ModalRootState(coroutineScope, navController) - } -} - -internal class Web3ModalRootState( - private val coroutineScope: CoroutineScope, - private val navController: NavController -) { - private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() - val currentDestinationFlow: Flow - get() = navController.currentBackStackEntryFlow - - val canPopUp: Boolean - get() = !topLevelDestinations.any { it.path == navController.currentDestination?.route } || navController.previousBackStackEntry != null - - val title: Flow = currentDestinationFlow - .map { it.getTitleArg() ?: it.destination.toTitle() } - - val currentDestinationRoute: String? - get() = navController.currentDestination?.route - - fun navigateToHelp() { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_WALLET_HELP,)) - navController.navigate(Route.WHAT_IS_WALLET.path) - } - - fun popUp() { - navController.popBackStack() - } -} - -private fun NavDestination.toTitle(): String? = Route.values().find { route.orEmpty().startsWith(it.path) }?.title - -private val topLevelDestinations = listOf(Route.CONNECT_YOUR_WALLET, Route.ACCOUNT, Route.CHOOSE_NETWORK, Route.CHANGE_NETWORK, Route.SIWE_FALLBACK) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectListSelect.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectListSelect.kt deleted file mode 100644 index 5fca062eb..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectListSelect.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.walletconnect - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.ui.components.internal.commons.ListSelectRow -import com.walletconnect.web3.modal.ui.components.internal.commons.AllWalletsIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.TextLabel -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview - -fun LazyListScope.allWallets(text: String, isEnabled: Boolean = true, onClick: () -> Unit) { - item { WalletConnectAll(text = text, isEnabled = isEnabled, onClick = onClick) } -} - -@Composable -private fun WalletConnectAll(text: String, isEnabled: Boolean = true, onClick: () -> Unit) { - ListSelectRow( - startIcon = { AllWalletsIcon() }, - text = "All wallets", - label = { TextLabel(text = text, isEnabled = isEnabled) }, - onClick = onClick, - contentPadding = PaddingValues(vertical = 4.dp) - ) -} - -@UiModePreview -@Composable -private fun WalletConnectListSelectPreview() { - MultipleComponentsPreview( - { WalletConnectAll(text = "240+") {} }, - { WalletConnectAll(text = "240+", isEnabled = false) {} }, - ) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectLogo.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectLogo.kt deleted file mode 100644 index 4f8159283..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/components/internal/walletconnect/WalletConnectLogo.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.walletconnect.web3.modal.ui.components.internal.walletconnect - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.previews.MultipleComponentsPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun WalletConnectLogo( - isEnabled: Boolean = true -) { - val background: Color - val border: Color - val colorFilter: ColorFilter? - if (isEnabled) { - background = Web3ModalTheme.colors.accent100 - border = Web3ModalTheme.colors.grayGlass10 - colorFilter = null - } else { - background = Web3ModalTheme.colors.background.color300 - border = Web3ModalTheme.colors.grayGlass05 - colorFilter = ColorFilter.tint(Web3ModalTheme.colors.grayGlass30) - } - - Image( - modifier = Modifier - .size(40.dp) - .background(background, shape = RoundedCornerShape(8.dp)) - .border(width = 1.dp, color = border, shape = RoundedCornerShape(8.dp)) - .padding(4.dp), - imageVector = ImageVector.vectorResource(id = R.drawable.ic_wallet_connect_logo), - contentDescription = ContentDescription.WC_LOGO.description, - - colorFilter = colorFilter - ) -} - -@UiModePreview -@Composable -private fun PreviewWalletConnectLogo() { - MultipleComponentsPreview( - { WalletConnectLogo() }, - { WalletConnectLogo(false) } - ) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/model/UiState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/model/UiState.kt deleted file mode 100644 index e399c1019..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/model/UiState.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.walletconnect.web3.modal.ui.model - -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.togetherWith -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.ui.components.internal.ErrorModalState -import com.walletconnect.web3.modal.ui.components.internal.LoadingModalState -import kotlinx.coroutines.flow.StateFlow - -@Composable -internal fun UiStateBuilder( - uiStateFlow: StateFlow>, - onError: @Composable (Throwable) -> Unit = { ErrorModalState {} }, - onLoading: @Composable (T?) -> Unit = { LoadingModalState() }, - onSuccess: @Composable (T) -> Unit -) { - val uiState by uiStateFlow.collectAsState() - AnimatedContent( - targetState = uiState, - label = "UiStateBuilder $uiState", - transitionSpec = { fadeIn() + slideInHorizontally { it / 2 } togetherWith fadeOut() } - ) { state -> - when (state) { - is UiState.Error -> onError(state.error) - is UiState.Loading -> onLoading(state.data) - is UiState.Success -> onSuccess(state.data) - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Route.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Route.kt deleted file mode 100644 index c369a6907..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/Route.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.web3.modal.ui.navigation - -//Todo Think about split it into own graphs routes - -enum class Route(val path: String, val title: String? = null) { - //Common - WEB3MODAL("web3_modal"), - CHOOSE_NETWORK("choose_network", "Select network"), - //Connect routes - CONNECT_YOUR_WALLET("connect_your_wallet", "Connect wallet"), - QR_CODE("qr_code", "WalletConnect"), - WHAT_IS_WALLET("what_is_wallet", "What is a wallet?"), - GET_A_WALLET("get_a_wallet", "Get a wallet"), - ALL_WALLETS("all_wallets", "All wallets"), - REDIRECT("redirect"), - - //Session routes - ACCOUNT("account"), - CHANGE_NETWORK("change_network", "Select network"), - CHAIN_SWITCH_REDIRECT("chain_switch_redirect"), - RECENT_TRANSACTION("recent_transaction"), - WHAT_IS_NETWORK("what_is_network", "What is a network?"), - SIWE_FALLBACK("siwe_fallback", "Sign In") -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/account/Navigation.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/account/Navigation.kt deleted file mode 100644 index a353fbcca..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/account/Navigation.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.web3.modal.ui.navigation.account - -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType -import com.google.accompanist.navigation.animation.composable -import androidx.navigation.navArgument -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.addTitleArg -import com.walletconnect.web3.modal.ui.routes.account.AccountViewModel -import com.walletconnect.web3.modal.ui.routes.account.chain_redirect.ChainSwitchRedirectRoute - -private const val CHAIN_ID_KEY = "chainId" -private const val CHAIN_ID_ARG = "{chainId}" - -internal fun Modal.Model.Chain.toChainSwitchPath() = Route.CHAIN_SWITCH_REDIRECT.path + "/${id}&${chainName}" - -internal fun NavGraphBuilder.chainSwitchRoute( - accountViewModel: AccountViewModel -) { - composable( - route = Route.CHAIN_SWITCH_REDIRECT.path + "/" + CHAIN_ID_ARG + addTitleArg(), - arguments = listOf(navArgument(CHAIN_ID_KEY) { type = NavType.StringType } ) - ) { backStackEntry -> - val chainId = backStackEntry.arguments?.getString(CHAIN_ID_KEY, String.Empty) - val chain = Web3Modal.chains.find { it.id == chainId } - chain?.let { ChainSwitchRedirectRoute(accountViewModel = accountViewModel, chain = it) } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/connection/RedirectNavigation.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/connection/RedirectNavigation.kt deleted file mode 100644 index 287035a2a..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/navigation/connection/RedirectNavigation.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.web3.modal.ui.navigation.connection - -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType -import androidx.navigation.navArgument -import com.google.accompanist.navigation.animation.composable -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.addTitleArg -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.routes.connect.redirect.RedirectWalletRoute -import timber.log.Timber - -private const val WALLET_ID_KEY = "walletId" -private const val WALLET_ID_ARG = "{walletId}" - -internal fun Wallet.toRedirectPath() = Route.REDIRECT.path + "/${id}&${name}" - -internal fun NavGraphBuilder.redirectRoute( - connectViewModel: ConnectViewModel -) { - composable( - route = Route.REDIRECT.path + "/" + WALLET_ID_ARG + addTitleArg(), - arguments = listOf(navArgument(WALLET_ID_KEY) { type = NavType.StringType }) - ) { backStackEntry -> - val walletId = backStackEntry.arguments?.getString(WALLET_ID_KEY, String.Empty) - val wallet = connectViewModel.getWallet(walletId) - wallet?.let { RedirectWalletRoute(connectState = connectViewModel, wallet = it) } ?: Timber.e("Invalid wallet id") - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/Previews.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/Previews.kt deleted file mode 100644 index 6769107ab..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/previews/Previews.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.walletconnect.web3.modal.ui.previews - -import android.content.res.Configuration.UI_MODE_NIGHT_NO -import android.content.res.Configuration.UI_MODE_NIGHT_YES -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Devices -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.compose.rememberNavController -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.web3.modal.ui.components.internal.root.Web3ModalRoot -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.root.rememberWeb3ModalRootState -import com.walletconnect.web3.modal.ui.components.internal.snackbar.rememberSnackBarState -import com.walletconnect.web3.modal.ui.theme.ProvideWeb3ModalThemeComposition -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import org.koin.dsl.module - -@Composable -internal fun Web3ModalPreview( - title: String? = null, - content: @Composable () -> Unit, -) { - previewKoinDefinitions() - ProvideWeb3ModalThemeComposition { - val scope = rememberCoroutineScope() - val rootState = rememberWeb3ModalRootState(coroutineScope = scope, navController = rememberNavController()) - val snackBarState = rememberSnackBarState(coroutineScope = scope) - - Web3ModalRoot(rootState = rootState, snackBarState = snackBarState, closeModal = {}, title = title) { - content() - } - } -} - -@Composable -internal fun ComponentPreview( - content: @Composable () -> Unit -) { - previewKoinDefinitions() - ProvideWeb3ModalThemeComposition { - Column(modifier = Modifier.background(Web3ModalTheme.colors.background.color100)) { - content() - } - } -} - -@Composable -internal fun MultipleComponentsPreview( - vararg content: @Composable () -> Unit -) { - previewKoinDefinitions() - ProvideWeb3ModalThemeComposition { - Column { - content.forEach { - VerticalSpacer(height = 5.dp) - Box(modifier = Modifier.background(Web3ModalTheme.colors.background.color100)) { it() } - VerticalSpacer(height = 5.dp) - } - } - } -} - -private fun previewKoinDefinitions() { - val modules = listOf( - module { single { ProjectId("fakeId") } } - ) - wcKoinApp.koin.loadModules(modules = modules) -} - -@LightTheme -@DarkTheme -internal annotation class UiModePreview - -@Preview(uiMode = UI_MODE_NIGHT_NO) -internal annotation class LightTheme - -@Preview(uiMode = UI_MODE_NIGHT_YES) -internal annotation class DarkTheme - -@Preview(device = Devices.AUTOMOTIVE_1024p, widthDp = 720, heightDp = 360, uiMode = UI_MODE_NIGHT_YES) -internal annotation class Landscape diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountNavigationGraph.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountNavigationGraph.kt deleted file mode 100644 index dce5e4d11..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountNavigationGraph.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account - -import androidx.compose.runtime.Composable -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import com.walletconnect.web3.modal.ui.navigation.ConsumeNavigationEventsEffect -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.account.chainSwitchRoute -import com.walletconnect.web3.modal.ui.routes.account.account.AccountRoute -import com.walletconnect.web3.modal.ui.routes.account.change_network.ChangeNetworkRoute -import com.walletconnect.web3.modal.ui.routes.account.what_is_network.WhatIsNetworkRoute -import com.walletconnect.web3.modal.ui.utils.AnimatedNavGraph -import com.walletconnect.web3.modal.ui.utils.animatedComposable - -@Composable -internal fun AccountNavGraph( - navController: NavHostController, - closeModal: () -> Unit, - shouldOpenChangeNetwork: Boolean -) { - val startDestination = if (shouldOpenChangeNetwork) Route.CHANGE_NETWORK.path else Route.ACCOUNT.path - val accountViewModel = viewModel() - - ConsumeNavigationEventsEffect( - navController = navController, - navigator = accountViewModel, - closeModal = closeModal - ) - - AnimatedNavGraph( - navController = navController, - startDestination = startDestination - ) { - animatedComposable(route = Route.ACCOUNT.path) { - AccountRoute( - accountViewModel = accountViewModel, - navController = navController - ) - } - animatedComposable(route = Route.CHANGE_NETWORK.path) { - ChangeNetworkRoute(accountViewModel = accountViewModel) - } - animatedComposable(route = Route.WHAT_IS_WALLET.path) { - WhatIsNetworkRoute() - } - chainSwitchRoute(accountViewModel = accountViewModel) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountViewModel.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountViewModel.kt deleted file mode 100644 index 4e7672ae8..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/AccountViewModel.kt +++ /dev/null @@ -1,185 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.client.models.request.SentRequestResult -import com.walletconnect.web3.modal.domain.model.AccountData -import com.walletconnect.web3.modal.domain.model.Session -import com.walletconnect.web3.modal.domain.usecase.GetEthBalanceUseCase -import com.walletconnect.web3.modal.domain.usecase.GetIdentityUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.ObserveSessionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveChainSelectionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveSessionUseCase -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import com.walletconnect.web3.modal.engine.coinbase.CoinbaseResult -import com.walletconnect.web3.modal.ui.navigation.Navigator -import com.walletconnect.web3.modal.ui.navigation.NavigatorImpl -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.account.toChainSwitchPath -import com.walletconnect.web3.modal.utils.EthUtils -import com.walletconnect.web3.modal.utils.createAddEthChainParams -import com.walletconnect.web3.modal.utils.createSwitchChainParams -import com.walletconnect.web3.modal.utils.getChains -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch - -internal class AccountViewModel : ViewModel(), Navigator by NavigatorImpl() { - private val logger: Logger = wcKoinApp.koin.get() - - private val saveChainSelectionUseCase: SaveChainSelectionUseCase = wcKoinApp.koin.get() - private val saveSessionUseCase: SaveSessionUseCase = wcKoinApp.koin.get() - private val observeSessionUseCase: ObserveSessionUseCase = wcKoinApp.koin.get() - private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() - private val getIdentityUseCase: GetIdentityUseCase = wcKoinApp.koin.get() - private val getEthBalanceUseCase: GetEthBalanceUseCase = wcKoinApp.koin.get() - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() - private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() - private val activeSessionFlow = observeSessionUseCase() - - private val accountDataFlow = activeSessionFlow - .map { - if (web3ModalEngine.getAccount() != null) { - it - } else { - null - } - } - .map { activeSession -> - if (activeSession != null) { - val chains = activeSession.getChains() - val identity = getIdentityUseCase(activeSession.address, activeSession.chain) - accountData = AccountData( - address = activeSession.address, chains = chains, identity = identity - ) - UiState.Success(accountData) - } else { - UiState.Error(Throwable("Active session not found")) - } - }.catch { - showError(it.localizedMessage) - logger.error(it) - emit(UiState.Error(it)) - } - - lateinit var accountData: AccountData - - val accountState = accountDataFlow.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = UiState.Loading()) - - val selectedChain = observeSelectedChainUseCase().map { web3ModalEngine.getSelectedChainOrFirst() } - - val balanceState = combine(activeSessionFlow, selectedChain) { session, selectedChain -> - if (session != null && selectedChain.rpcUrl != null) { - return@combine getEthBalanceUseCase(selectedChain.token, selectedChain.rpcUrl, session.address) - } else { - null - } - }.flowOn(Dispatchers.IO).catch { logger.error(it) }.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = null) - - fun disconnect() { - web3ModalEngine.disconnect( - onSuccess = { closeModal() }, - onError = { - showError(it.localizedMessage) - logger.error(it) - } - ) - } - - fun changeActiveChain(chain: Modal.Model.Chain) = viewModelScope.launch { - if (accountData.chains.contains(chain)) { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SWITCH_NETWORK, Properties(network = chain.id))) - saveChainSelectionUseCase(chain.id) - popBackStack() - } else { - navigateTo(chain.toChainSwitchPath()) - } - } - - suspend fun updatedSessionAfterChainSwitch(updatedSession: Session) { - if (updatedSession.getChains().any { it.id == updatedSession.chain }) { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SWITCH_NETWORK, Properties(network = updatedSession.chain))) - saveSessionUseCase(updatedSession) - popBackStack(path = Route.CHANGE_NETWORK.path, inclusive = true) - } - } - - fun switchChain(to: Modal.Model.Chain, onReject: () -> Unit) { - val onError: (String?) -> Unit = { showError(it ?: "Something went wrong") } - val isChainApproved = accountData.chains.contains(to) - val onSuccess: (SentRequestResult) -> Unit = { it.handleRequestResult(to, onError, onReject) } - if (!isChainApproved && to.optionalMethods.contains(EthUtils.walletAddEthChain)) { - addEthChain(to, onSuccess, onError) - } else { - switchEthChain(to, onSuccess, onError) - } - } - - private fun SentRequestResult.handleRequestResult( - to: Modal.Model.Chain, - onError: (String?) -> Unit, - onReject: () -> Unit - ) { - when (this) { - is SentRequestResult.Coinbase -> this.results.firstOrNull()?.let { - when (it) { - is CoinbaseResult.Error -> { - onError(it.message) - onReject() - } - - is CoinbaseResult.Result -> { - viewModelScope.launch { - updatedSessionAfterChainSwitch(Session.Coinbase(to.id, accountData.address)) - logger.log("Successful request: ${it.value}") - } - } - } - } - - is SentRequestResult.WalletConnect -> logger.log("Successful request: ${this.requestId}") - } - } - - private fun switchEthChain( - to: Modal.Model.Chain, - onSuccess: (SentRequestResult) -> Unit, - onError: (String?) -> Unit - ) { - web3ModalEngine.request( - Request(method = EthUtils.walletSwitchEthChain, params = createSwitchChainParams(to)), - onSuccess - ) { onError(it.message) } - } - - private fun addEthChain( - to: Modal.Model.Chain, onSuccess: (SentRequestResult) -> Unit, onError: (String?) -> Unit - ) { - web3ModalEngine.request( - Request(method = EthUtils.walletAddEthChain, params = createAddEthChainParams(to)), - onSuccess - ) { onError(it.message) } - } - - fun getSelectedChainOrFirst() = web3ModalEngine.getSelectedChainOrFirst() - - fun navigateToHelp() { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORK_HELP)) - navigateTo(Route.WHAT_IS_WALLET.path) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/account/AccountRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/account/AccountRoute.kt deleted file mode 100644 index 8546a32a4..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/account/AccountRoute.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account.account - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.model.AccountData -import com.walletconnect.web3.modal.domain.model.Balance -import com.walletconnect.web3.modal.ui.components.internal.commons.CloseIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.CompassIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.DisconnectIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.account.AccountName -import com.walletconnect.web3.modal.ui.components.internal.commons.account.AccountImage -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ChipButton -import com.walletconnect.web3.modal.ui.components.internal.commons.entry.AccountEntry -import com.walletconnect.web3.modal.ui.components.internal.commons.entry.AccountEntryState -import com.walletconnect.web3.modal.ui.components.internal.commons.network.CircleNetworkImage -import com.walletconnect.web3.modal.ui.model.UiStateBuilder -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.ethereumChain -import com.walletconnect.web3.modal.ui.routes.account.AccountViewModel -import com.walletconnect.web3.modal.ui.previews.accountDataPreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.utils.getImageData - -@Composable -internal fun AccountRoute( - navController: NavController, - accountViewModel: AccountViewModel -) { - val uriHandler = LocalUriHandler.current - val selectedChain by accountViewModel.selectedChain.collectAsState(initial = accountViewModel.getSelectedChainOrFirst()) - val balance by accountViewModel.balanceState.collectAsState() - - Box(modifier = Modifier.fillMaxWidth()) { - UiStateBuilder(uiStateFlow = accountViewModel.accountState) { data -> - AccountScreen( - accountData = data, - selectedChain = selectedChain, - balance = balance, - onBlockExplorerClick = { url -> uriHandler.openUri(url) }, - onChangeNetworkClick = { navController.navigate(Route.CHANGE_NETWORK.path) }, - onDisconnectClick = { accountViewModel.disconnect() } - ) - } - CloseIcon( - modifier = Modifier - .align(Alignment.TopEnd) - .padding(18.dp), - onClick = { accountViewModel.closeModal() } - ) - } -} - -@Composable -private fun AccountScreen( - accountData: AccountData, - selectedChain: Modal.Model.Chain, - balance: Balance?, - onBlockExplorerClick: (String) -> Unit, - onChangeNetworkClick: () -> Unit, - onDisconnectClick: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .verticalScroll(rememberScrollState()) - .padding( - top = 32.dp, bottom = 16.dp, start = 12.dp, end = 12.dp - ), - horizontalAlignment = Alignment.CenterHorizontally - ) { - AccountImage(address = accountData.address, avatarUrl = accountData.identity?.avatar) - VerticalSpacer(height = 20.dp) - AccountName(accountData) - balance?.let { balance -> - VerticalSpacer(height = 4.dp) - Text( - text = balance.valueWithSymbol, - style = Web3ModalTheme.typo.paragraph400.copy(Web3ModalTheme.colors.foreground.color200) - ) - } - selectedChain.blockExplorerUrl?.let { url -> - VerticalSpacer(height = 12.dp) - ChipButton( - text = "Block Explorer", - startIcon = { CompassIcon() }, - endIcon = { ExternalIcon(it) }, - style = ButtonStyle.SHADE, - size = ButtonSize.S, - onClick = { onBlockExplorerClick("$url/address/${accountData.address}") } - ) - } - VerticalSpacer(height = 20.dp) - AccountEntry( - startIcon = { CircleNetworkImage(selectedChain.getImageData()) }, - onClick = onChangeNetworkClick, - state = AccountEntryState.NEXT - ) { - Text(text = selectedChain.chainName, style = Web3ModalTheme.typo.paragraph500.copy(color = it.textColor)) - } - VerticalSpacer(height = 8.dp) - AccountEntry( - startIcon = { DisconnectIcon() }, - onClick = onDisconnectClick - ) { - Text(text = "Disconnect", style = Web3ModalTheme.typo.paragraph500.copy(color = it.textColor)) - } - } -} - -@UiModePreview -@Composable -private fun PreviewAccountScreen() { - Web3ModalPreview { - AccountScreen(accountDataPreview, ethereumChain,null, {}, {}, {}) - } -} - -@UiModePreview -@Composable -private fun PreviewAccountScreenWithBalance() { - Web3ModalPreview { - AccountScreen(accountDataPreview, ethereumChain, Balance(ethereumChain.token, "0000000"), {}, {}, {}) - } -} - diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainRedirectState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainRedirectState.kt deleted file mode 100644 index 1f193650d..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/chain_redirect/ChainRedirectState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account.chain_redirect - -internal sealed class ChainRedirectState { - object Loading : ChainRedirectState() - - object Declined: ChainRedirectState() -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/change_network/ChangeNetworkRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/change_network/ChangeNetworkRoute.kt deleted file mode 100644 index 2403ed987..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/change_network/ChangeNetworkRoute.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account.change_network - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.itemsIndexed -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.ui.components.internal.commons.NetworkBottomSection -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.network.ChainNetworkItem -import com.walletconnect.web3.modal.ui.model.UiStateBuilder -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.ethereumChain -import com.walletconnect.web3.modal.ui.previews.testChains -import com.walletconnect.web3.modal.ui.routes.account.AccountViewModel -import com.walletconnect.web3.modal.utils.getChainNetworkImageUrl - -@Composable -internal fun ChangeNetworkRoute( - accountViewModel: AccountViewModel -) { - val selectedChain by accountViewModel.selectedChain.collectAsState(initial = accountViewModel.getSelectedChainOrFirst()) - - UiStateBuilder(uiStateFlow = accountViewModel.accountState) { - ChangeNetworkScreen( - chains = Web3Modal.chains, - selectedChain = selectedChain, - onChainItemClick = { accountViewModel.changeActiveChain(it) }, - onWhatIsWalletClick = { accountViewModel.navigateToHelp() } - ) - } -} - -@Composable -private fun ChangeNetworkScreen( - chains: List, - selectedChain: Modal.Model.Chain, - onChainItemClick: (Modal.Model.Chain) -> Unit, - onWhatIsWalletClick: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 12.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - VerticalSpacer(height = 8.dp) - ChainNetworkGrid( - chains = chains, - selectedChain = selectedChain, - onItemClick = { onChainItemClick(it) } - ) - NetworkBottomSection(onWhatIsWalletClick) - } -} - -@Composable -private fun ChainNetworkGrid( - chains: List, - selectedChain: Modal.Model.Chain, - onItemClick: (Modal.Model.Chain) -> Unit -) { - LazyVerticalGrid( - columns = GridCells.FixedSize(82.dp), - modifier = Modifier.padding(horizontal = 10.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalArrangement = Arrangement.Center, - content = { - itemsIndexed(chains) { _, item -> - ChainNetworkItem( - isSelected = item.id == selectedChain.id, - networkName = item.chainName, - image = item.chainImage ?: getChainNetworkImageUrl(item.chainReference) - ) { - onItemClick(item) - } - } - } - ) -} - -@Composable -@UiModePreview -private fun ChangeNetworkPreview() { - Web3ModalPreview("Change Network") { - ChangeNetworkScreen(testChains, ethereumChain, {}, {}) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt deleted file mode 100644 index 2117856e0..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/account/what_is_network/WhatIsNetworkRoute.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account.what_is_network - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HelpSection -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun WhatIsNetworkRoute() { - val uriHandler = LocalUriHandler.current - - WhatIsNetwork { uriHandler.openUri("https://ethereum.org/en/developers/docs/networks/") } -} - -@Composable -private fun WhatIsNetwork( - onLearnMoreClick: () -> Unit -) { - Column( - modifier = Modifier - .padding(horizontal = 20.dp) - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally - ) { - VerticalSpacer(20.dp) - HelpSection( - title = "The system's nuts and bolts", - body = "A network is what brings the blockchain to life, as this technical infrastructure allows apps to access the ledger and smart contract services.", - assets = listOf(R.drawable.network, R.drawable.layers, R.drawable.system) - ) - VerticalSpacer(24.dp) - HelpSection( - title = "Designed for different uses", - body = "Each network is designed differently, and may therefore suit certain apps and experiences.", - assets = listOf(R.drawable.noun, R.drawable.defi_alt, R.drawable.dao) - ) - VerticalSpacer(height = 20.dp) - ImageButton( - text = "Learn more", - image = { ExternalIcon(Web3ModalTheme.colors.inverse100) }, - style = ButtonStyle.MAIN, - size = ButtonSize.S, - paddingValues = PaddingValues(start = 8.dp, top = 6.dp, end = 12.dp, 6.dp), - onClick = { onLearnMoreClick() } - ) - Spacer(modifier = Modifier.height(30.dp)) - } -} - -@UiModePreview -@Composable -private fun WhatIsNetworkPreview() { - Web3ModalPreview { - WhatIsNetworkRoute() - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/common/WhatIsNetwork.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/common/WhatIsNetwork.kt deleted file mode 100644 index 866c7834e..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/common/WhatIsNetwork.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.common - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.unit.dp -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.HelpSection -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun WhatIsNetworkRoute() { - val uriHandler = LocalUriHandler.current - - WhatIsNetwork { - uriHandler.openUri("https://ethereum.org/en/developers/docs/networks/") - } -} - -@Composable -private fun WhatIsNetwork( - onLearnMoreClick: () -> Unit -) { - Column( - modifier = Modifier.padding(horizontal = 20.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Spacer(modifier = Modifier.height(20.dp)) - HelpSection( - title = "The system’s nuts and bolts", - body = "A network is what brings the blockchain to life, as this technical infrastructure allows apps to access the ledger and smart contract services.", - assets = listOf(R.drawable.network, R.drawable.layers, R.drawable.system) - ) - Spacer(modifier = Modifier.height(4.dp)) - HelpSection( - title = "Designed for different uses", - body = "Each network is designed differently, and may therefore suit certain apps and experiences.", - assets = listOf(R.drawable.noun, R.drawable.defi_alt, R.drawable.dao) - ) - Spacer(modifier = Modifier.height(10.dp)) - ImageButton( - text = "Learn more", - image = { ExternalIcon(Web3ModalTheme.colors.accent100) }, - style = ButtonStyle.MAIN, - size = ButtonSize.S, - onClick = onLearnMoreClick - ) - Spacer(modifier = Modifier.height(30.dp)) - } -} - -@UiModePreview -@Composable -private fun WhatIsNetworkPreview() { - Web3ModalPreview(title = "What is a network?") { - WhatIsNetworkRoute() - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectNavigationGraph.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectNavigationGraph.kt deleted file mode 100644 index bace95d4d..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectNavigationGraph.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import androidx.compose.runtime.Composable -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import com.walletconnect.web3.modal.ui.navigation.ConsumeNavigationEventsEffect -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.connection.redirectRoute -import com.walletconnect.web3.modal.ui.routes.account.siwe_fallback.SIWEFallbackRoute -import com.walletconnect.web3.modal.ui.routes.common.WhatIsNetworkRoute -import com.walletconnect.web3.modal.ui.routes.connect.all_wallets.AllWalletsRoute -import com.walletconnect.web3.modal.ui.routes.connect.choose_network.ChooseNetworkRoute -import com.walletconnect.web3.modal.ui.routes.connect.connect_wallet.ConnectWalletRoute -import com.walletconnect.web3.modal.ui.routes.connect.get_wallet.GetAWalletRoute -import com.walletconnect.web3.modal.ui.routes.connect.scan_code.ScanQRCodeRoute -import com.walletconnect.web3.modal.ui.routes.connect.what_is_wallet.WhatIsWallet -import com.walletconnect.web3.modal.ui.utils.AnimatedNavGraph -import com.walletconnect.web3.modal.ui.utils.animatedComposable - -@Composable -internal fun ConnectionNavGraph( - navController: NavHostController, - closeModal: () -> Unit, - shouldOpenChooseNetwork: Boolean -) { - val connectViewModel = viewModel() - val startDestination = if (shouldOpenChooseNetwork) { - Route.CHOOSE_NETWORK.path - } else { - Route.CONNECT_YOUR_WALLET.path - } - - ConsumeNavigationEventsEffect( - navController = navController, - navigator = connectViewModel, - closeModal = closeModal - ) - - AnimatedNavGraph( - navController = navController, - startDestination = startDestination - ) { - animatedComposable(route = Route.SIWE_FALLBACK.path) { - SIWEFallbackRoute(connectViewModel = connectViewModel) - } - animatedComposable(route = Route.CONNECT_YOUR_WALLET.path) { - ConnectWalletRoute(connectViewModel = connectViewModel) - } - animatedComposable(route = Route.QR_CODE.path) { - ScanQRCodeRoute(connectViewModel = connectViewModel) - } - animatedComposable(route = Route.WHAT_IS_WALLET.path) { - WhatIsWallet(navController = navController) - } - animatedComposable(Route.GET_A_WALLET.path) { - GetAWalletRoute(wallets = connectViewModel.getNotInstalledWallets()) - } - animatedComposable(Route.ALL_WALLETS.path) { - AllWalletsRoute(connectViewModel = connectViewModel) - } - redirectRoute(connectViewModel) - animatedComposable(Route.CHOOSE_NETWORK.path) { - ChooseNetworkRoute(connectViewModel = connectViewModel) - } - animatedComposable(Route.WHAT_IS_NETWORK.path) { - WhatIsNetworkRoute() - } - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectViewModel.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectViewModel.kt deleted file mode 100644 index d31016185..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectViewModel.kt +++ /dev/null @@ -1,242 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.ConnectionMethod -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.client.models.request.SentRequestResult -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.domain.usecase.ObserveSelectedChainUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveChainSelectionUseCase -import com.walletconnect.web3.modal.domain.usecase.SaveRecentWalletUseCase -import com.walletconnect.web3.modal.engine.Web3ModalEngine -import com.walletconnect.web3.modal.ui.navigation.Navigator -import com.walletconnect.web3.modal.ui.navigation.NavigatorImpl -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.navigation.connection.toRedirectPath -import com.walletconnect.web3.modal.utils.getSelectedChain -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch - -internal class ConnectViewModel : ViewModel(), Navigator by NavigatorImpl(), ParingController by PairingControllerImpl() { - private val logger: Logger = wcKoinApp.koin.get() - private val walletsDataStore = WalletDataSource { showError(it) } - private val saveRecentWalletUseCase: SaveRecentWalletUseCase = wcKoinApp.koin.get() - private val saveChainSelectionUseCase: SaveChainSelectionUseCase = wcKoinApp.koin.get() - private val observeSelectedChainUseCase: ObserveSelectedChainUseCase = wcKoinApp.koin.get() - private val web3ModalEngine: Web3ModalEngine = wcKoinApp.koin.get() - private val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() - private var sessionParams = getSessionParamsSelectedChain(Web3Modal.selectedChain?.id) - val selectedChain = observeSelectedChainUseCase().map { savedChainId -> - Web3Modal.chains.find { it.id == savedChainId } ?: web3ModalEngine.getSelectedChainOrFirst() - } - private var _isConfirmLoading: MutableStateFlow = MutableStateFlow(false) - val isConfirmLoading get() = _isConfirmLoading.asStateFlow() - private var _isCancelLoading: MutableStateFlow = MutableStateFlow(false) - val isCancelLoading get() = _isCancelLoading.asStateFlow() - var wallet: Wallet? = null - - val walletsState: StateFlow = walletsDataStore.searchWalletsState.stateIn(viewModelScope, SharingStarted.Lazily, WalletsData.empty()) - val uiState: StateFlow>> = walletsDataStore.walletState.map { pagingData -> - when { - pagingData.error != null -> UiState.Error(pagingData.error) - pagingData.loadingState == LoadingState.REFRESH -> UiState.Loading() - else -> UiState.Success(pagingData.wallets) - } - }.stateIn(viewModelScope, started = SharingStarted.Lazily, initialValue = UiState.Loading()) - - val searchPhrase - get() = walletsDataStore.searchPhrase - - init { - Web3ModalDelegate - .wcEventModels - .filterIsInstance() - .onEach { - _isConfirmLoading.value = false - showError(it.message) - disconnect() - }.launchIn(viewModelScope) - - fetchInitialWallets() - } - - fun disconnect() { - _isCancelLoading.value = true - web3ModalEngine.disconnect( - onSuccess = { - _isCancelLoading.value = false - closeModal() - }, - onError = { - _isCancelLoading.value = false - showError(it.localizedMessage) - logger.error(it) - } - ) - } - - fun sendSIWEOverPersonalSign() { - _isConfirmLoading.value = true - web3ModalEngine.shouldDisconnect = false - val account = web3ModalEngine.getAccount() ?: throw IllegalStateException("Account is null") - val issuer = "did:pkh:${account.chain.id}:${account.address}" - val siweMessage = web3ModalEngine.formatSIWEMessage(Web3Modal.authPayloadParams!!, issuer) - val msg = siweMessage.encodeToByteArray().joinToString(separator = "", prefix = "0x") { eachByte -> "%02x".format(eachByte) } - val body = "[\"$msg\", \"${account.address}\"]" - web3ModalEngine.request( - request = Request("personal_sign", body), - onSuccess = { sendRequest -> - logger.log("SIWE sent successfully") - web3ModalEngine.siweRequestIdWithMessage = Pair((sendRequest as SentRequestResult.WalletConnect).requestId, siweMessage) - }, - onError = { - if (it !is Web3ModalEngine.RedirectMissingThrowable) { - web3ModalEngine.shouldDisconnect = true - } - - _isConfirmLoading.value = false - showError(it.message) - }, - ) - } - - fun fetchInitialWallets() { - viewModelScope.launch { walletsDataStore.fetchInitialWallets() } - } - - fun navigateToHelp() { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_NETWORK_HELP)) - navigateTo(Route.WHAT_IS_WALLET.path) - } - - fun navigateToScanQRCode() { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SELECT_WALLET, Properties(name = "WalletConnect", platform = ConnectionMethod.QR_CODE))) - connectWalletConnect(name = "WalletConnect", method = ConnectionMethod.QR_CODE, linkMode = null) { navigateTo(Route.QR_CODE.path) } - } - - fun navigateToRedirectRoute(wallet: Wallet) { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.SELECT_WALLET, Properties(name = wallet.name, platform = wallet.toConnectionType()))) - saveRecentWalletUseCase(wallet.id) - walletsDataStore.updateRecentWallet(wallet.id) - navigateTo(wallet.toRedirectPath()) - } - - fun navigateToConnectWallet(chain: Modal.Model.Chain) { - viewModelScope.launch { saveChainSelectionUseCase(chain.id) } - sessionParams = getSessionParamsSelectedChain(chain.id) - navigateTo(Route.CONNECT_YOUR_WALLET.path) - } - - fun navigateToAllWallets() { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_ALL_WALLETS)) - clearSearch() - navigateTo(Route.ALL_WALLETS.path) - } - - fun connectWalletConnect(name: String, method: String, linkMode: String?, onSuccess: (String) -> Unit) { - if (Web3Modal.authPayloadParams != null) { - authenticate( - name, method, - walletAppLink = linkMode, - authParams = if (Web3Modal.selectedChain != null) Web3Modal.authPayloadParams!!.copy(chains = listOf(Web3Modal.selectedChain!!.id)) else Web3Modal.authPayloadParams!!, - onSuccess = { onSuccess(it) }, - onError = { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = it.message ?: "Relay error while connecting"))) - showError(it.localizedMessage) - logger.error(it) - } - ) - } else { - connect( - name, method, - sessionParams = sessionParams, - onSuccess = onSuccess, - onError = { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CONNECT_ERROR, Properties(message = it.message ?: "Relay error while connecting"))) - showError(it.localizedMessage) - logger.error(it) - } - ) - } - } - - fun connectCoinbase(onSuccess: () -> Unit = {}) { - web3ModalEngine.connectCoinbase( - onSuccess = onSuccess, - onError = { - showError(it.localizedMessage) - logger.error(it) - } - ) - } - - fun fetchMoreWallets() { - viewModelScope.launch { walletsDataStore.fetchMoreWallets() } - } - - fun search(searchPhrase: String) { - viewModelScope.launch { walletsDataStore.searchWallet(searchPhrase) } - } - - fun clearSearch() = walletsDataStore.clearSearch() - - fun getWallet(walletId: String?) = walletsDataStore.getWallet(walletId).also { wallet = it } - - fun getNotInstalledWallets() = walletsDataStore.wallets.filterNot { it.isWalletInstalled } - - fun getWalletsTotalCount() = walletsDataStore.totalWalletsCount - - private fun Wallet.toConnectionType(): String { - if (isWalletInstalled) ConnectionMethod.MOBILE - - return when { - hasMobileWallet && hasWebApp -> ConnectionMethod.UNDEFINED - hasMobileWallet -> ConnectionMethod.MOBILE - hasWebApp -> ConnectionMethod.WEB - else -> ConnectionMethod.UNDEFINED - } - } - - private fun getSessionParamsSelectedChain(chainId: String?) = with(Web3Modal.chains) { - val selectedChain = getSelectedChain(chainId) - Modal.Params.SessionParams( - requiredNamespaces = mapOf( - selectedChain.chainNamespace to Modal.Model.Namespace.Proposal( - chains = listOf(selectedChain.id), - methods = selectedChain.requiredMethods, - events = selectedChain.events - ) - ), - optionalNamespaces = filter { it.id != selectedChain.id }.toOptionalNamespaces() - ) - } - - private fun List.toOptionalNamespaces() = groupBy { it.chainNamespace } - .map { (key: String, value: List) -> - key to Modal.Model.Namespace.Proposal( - chains = value.map { it.id }, - methods = value.flatMap { it.requiredMethods + it.optionalMethods }.distinct(), - events = value.flatMap { it.events }.distinct() - ) - }.toMap() -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/all_wallets/AllWalletsRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/all_wallets/AllWalletsRoute.kt deleted file mode 100644 index 0f871fef2..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/all_wallets/AllWalletsRoute.kt +++ /dev/null @@ -1,310 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.all_wallets - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridScope -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.rememberLazyGridState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.utils.isLandscape -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.ContentDescription -import com.walletconnect.web3.modal.ui.components.internal.commons.HorizontalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.LoadingSpinner -import com.walletconnect.web3.modal.ui.components.internal.commons.ScanQRIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.TransparentSurface -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.SearchInput -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.SearchState -import com.walletconnect.web3.modal.ui.components.internal.commons.inputs.SearchStatePreviewProvider -import com.walletconnect.web3.modal.ui.components.internal.commons.walletsGridItems -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.web3.modal.ui.previews.ComponentPreview -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.routes.connect.WalletsData -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import com.walletconnect.web3.modal.ui.utils.conditionalModifier -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch - -@Composable -internal fun AllWalletsRoute( - connectViewModel: ConnectViewModel -) { - val walletsState by connectViewModel.walletsState.collectAsState() - - AllWalletsContent( - walletsData = walletsState, - searchPhrase = connectViewModel.searchPhrase, - onSearch = { connectViewModel.search(it) }, - onSearchClear = { connectViewModel.clearSearch() }, - onFetchNextPage = { connectViewModel.fetchMoreWallets() }, - onWalletItemClick = { wallet -> connectViewModel.navigateToRedirectRoute(wallet) }, - onScanQRClick = { connectViewModel.navigateToScanQRCode() } - ) -} - -@Composable -private fun AllWalletsContent( - walletsData: WalletsData, - searchPhrase: String, - onSearch: (String) -> Unit, - onSearchClear: () -> Unit, - onFetchNextPage: () -> Unit, - onWalletItemClick: (Wallet) -> Unit, - onScanQRClick: () -> Unit -) { - val gridState = rememberLazyGridState() - val coroutineScope = rememberCoroutineScope() - val scrollToFirstItem = { coroutineScope.launch { gridState.scrollToItem(0) } } - val searchState = remember { - SearchState( - searchPhrase = searchPhrase, - onSearchSubmit = { onSearch(it).also { scrollToFirstItem() } }, - onClearInput = { onSearchClear().also { scrollToFirstItem() } } - ) - } - val gridFraction = if (isLandscape) 1f else .95f - - LaunchedEffect(gridState) { - snapshotFlow { gridState.firstVisibleItemIndex != 0 && !gridState.canScrollForward } - .distinctUntilChanged() - .filter { it } - .collect { onFetchNextPage() } - } - - Column(modifier = Modifier.fillMaxHeight(gridFraction)) { - SearchInputRow(searchState, onScanQRClick) - if (walletsData.loadingState == LoadingState.REFRESH) { - Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - LoadingSpinner() - } - } else if (walletsData.wallets.isEmpty()) { - NoWalletsFoundItem() - } else { - WalletsGrid(gridState, walletsData, onWalletItemClick) - } - } -} - -@Composable -private fun WalletsGrid( - gridState: LazyGridState, - walletsData: WalletsData, - onWalletItemClick: (Wallet) -> Unit -) { - val color = Web3ModalTheme.colors.background.color275 - Box { - LazyVerticalGrid( - state = gridState, - columns = GridCells.FixedSize(82.dp), - modifier = Modifier - .padding(horizontal = 10.dp) - .graphicsLayer { alpha = 0.99f } - .drawWithContent { - val colors = listOf(Color.Transparent, color) - drawContent() - drawRect( - brush = Brush.verticalGradient(colors, startY = 0f, endY = 40f), - blendMode = BlendMode.DstIn, - ) - }, - verticalArrangement = Arrangement.Center, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - walletsGridItems(walletsData.wallets, onWalletItemClick) - if (walletsData.loadingState == LoadingState.APPEND) { - loadingWalletsItems() - } - } - } -} - -private fun LazyGridScope.loadingWalletsItems() { - items(10) { - TransparentSurface( - modifier = Modifier.padding(4.dp), - shape = RoundedCornerShape(16.dp) - ) { - Column( - modifier = Modifier - .width(76.dp) - .height(96.dp) - .background(Web3ModalTheme.colors.grayGlass02), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Image( - painter = painterResource(id = R.drawable.wallet_placeholder), - contentDescription = "Wallet loader", - modifier = Modifier - .size(54.dp) - .clip(RoundedCornerShape(16.dp)) - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass10, shape = RoundedCornerShape(16.dp)) - - ) - VerticalSpacer(height = 8.dp) - Text( - text = String.Empty, - style = Web3ModalTheme.typo.tiny500, - textAlign = TextAlign.Center, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 2.dp) - ) - } - } - - } -} - -@Composable -private fun SearchInputRow( - searchState: SearchState, - onScanQRClick: () -> Unit -) { - val defaultSpacing: Dp = 12.dp - val focusBorderWidth: Dp = 4.dp - val focusedSpacing: Dp = defaultSpacing - focusBorderWidth - val focusBorderColor = Web3ModalTheme.colors.accent20 - val state by searchState.state.collectAsState() - - val paddingValues: PaddingValues - val spacerValue: Dp - if (state.isFocused) { - spacerValue = focusedSpacing - paddingValues = PaddingValues(start = focusedSpacing, top = focusedSpacing, bottom = focusedSpacing, end = defaultSpacing) - } else { - spacerValue = 12.dp - paddingValues = PaddingValues(12.dp) - } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(paddingValues) - ) { - Box(modifier = Modifier - .weight(1f) - .conditionalModifier(state.isFocused) { - border(width = focusBorderWidth, color = focusBorderColor, RoundedCornerShape(16.dp)).padding(focusBorderWidth) - }) { - SearchInput(searchState) - } - HorizontalSpacer(width = spacerValue) - ScanQRIcon(onClick = onScanQRClick) - } -} - - -@Composable -private fun ColumnScope.NoWalletsFoundItem() { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Image( - imageVector = ImageVector.vectorResource(R.drawable.ic_wallet), - contentDescription = ContentDescription.WALLET.description, - modifier = Modifier - .size(40.dp) - .background(Web3ModalTheme.colors.grayGlass05, RoundedCornerShape(12.dp)) - .padding(7.dp) - ) - VerticalSpacer(height = 20.dp) - Text( - text = "No Wallet found", - style = TextStyle(color = Web3ModalTheme.colors.foreground.color125, fontSize = 16.sp), - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - } - -} - -@UiModePreview -@Composable -private fun SearchRowPreview( - @PreviewParameter(SearchStatePreviewProvider::class) state: SearchState -) { - ComponentPreview { SearchInputRow(searchState = state, {}) } -} - -@UiModePreview -@Composable -private fun AllWalletsEmptyPreview() { - Web3ModalPreview { - AllWalletsContent(WalletsData.empty(), "", {}, {}, {}, {}, {}) - } -} - -@UiModePreview -@Composable -private fun AllWalletsPreview() { - Web3ModalPreview { - AllWalletsContent(WalletsData.submit(testWallets),"", {}, {}, {}, {}, {}) - } -} - -@UiModePreview -@Composable -private fun AllWalletsLoadingRefreshPreview() { - Web3ModalPreview { - AllWalletsContent(WalletsData.refresh(),"", {}, {}, {}, {}, {}) - } -} - -@UiModePreview -@Composable -private fun AllWalletsLoadingAppendPreview() { - Web3ModalPreview { - AllWalletsContent(WalletsData.append(testWallets),"", {}, {}, {}, {}, {}) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt deleted file mode 100644 index aa9895d44..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/connect_wallet/ConnectWalletRoute.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.connect_wallet - -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.web3.modal.ui.components.internal.ErrorModalState -import com.walletconnect.web3.modal.ui.components.internal.commons.InstalledWalletIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ListSelectRow -import com.walletconnect.web3.modal.ui.components.internal.commons.RecentLabel -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletImage -import com.walletconnect.web3.modal.ui.components.internal.walletconnect.allWallets -import com.walletconnect.web3.modal.ui.model.UiStateBuilder -import com.walletconnect.web3.modal.ui.previews.ConnectYourWalletPreviewProvider -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun ConnectWalletRoute( - connectViewModel: ConnectViewModel -) { - UiStateBuilder( - connectViewModel.uiState, - onError = { ErrorModalState { connectViewModel.fetchInitialWallets() } } - ) { - ConnectWalletContent( - wallets = it, - walletsTotalCount = connectViewModel.getWalletsTotalCount(), - onWalletItemClick = { wallet -> connectViewModel.navigateToRedirectRoute(wallet) }, - onViewAllClick = { connectViewModel.navigateToAllWallets() }, - ) - } -} - -@Composable -private fun ConnectWalletContent( - wallets: List, - walletsTotalCount: Int, - onWalletItemClick: (Wallet) -> Unit, - onViewAllClick: () -> Unit, -) { - WalletsList( - wallets = wallets, - walletsTotalCount = walletsTotalCount, - onWalletItemClick = onWalletItemClick, - onViewAllClick = onViewAllClick, - ) -} - -@Composable -private fun WalletsList( - wallets: List, - walletsTotalCount: Int, - onWalletItemClick: (Wallet) -> Unit, - onViewAllClick: () -> Unit -) { - LazyColumn( - modifier = Modifier.fillMaxWidth(), - contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp) - ) { - itemsIndexed(items = wallets.take(4)) { _, item -> - WalletListSelect(item, onWalletItemClick) - } - allWallets(text = walletSizeLabel(walletsTotalCount), onClick = onViewAllClick) - } -} - -private fun walletSizeLabel(total: Int): String = with(total % 10) { - if (this != 0) { - "${total - this}+" - } else { - total.toString() - } -} - -@Composable -private fun WalletListSelect(item: Wallet, onWalletItemClick: (Wallet) -> Unit) { - val label: (@Composable (Boolean) -> Unit)? = when { - item.isRecent -> { - { RecentLabel(it) } - } - else -> null - } - - ListSelectRow( - startIcon = { - Box { - WalletImage( - url = item.imageUrl, - modifier = Modifier - .size(40.dp) - .border(width = 1.dp, color = Web3ModalTheme.colors.grayGlass10, shape = RoundedCornerShape(12.dp)) - .clip(RoundedCornerShape(12.dp)) - ) - if (item.isWalletInstalled) { - InstalledWalletIcon() - } - } - }, - text = item.name, - onClick = { onWalletItemClick(item) }, - contentPadding = PaddingValues(vertical = 4.dp), - label = label - ) -} - -@UiModePreview -@Composable -private fun ConnectYourWalletPreview( - @PreviewParameter(ConnectYourWalletPreviewProvider::class) wallets: List -) { - Web3ModalPreview(title = "Connect Wallet") { - ConnectWalletContent(wallets, 200, {}, {}) - } -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/get_wallet/GetAWalletRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/get_wallet/GetAWalletRoute.kt deleted file mode 100644 index 5ac4e4261..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/get_wallet/GetAWalletRoute.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.get_wallet - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.unit.dp -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.utils.openPlayStore -import com.walletconnect.web3.modal.ui.components.internal.commons.AllWalletsIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ExternalIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.ListSelectRow -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletImage -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.previews.testWallets - -@Composable -internal fun GetAWalletRoute(wallets: List) { - GetAWalletContent( - wallets = wallets, - ) -} - -@Composable -private fun GetAWalletContent( - wallets: List, -) { - val uriHandler = LocalUriHandler.current - - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 12.dp) - ) { - itemsIndexed(wallets.take(5)) { _, wallet -> - ListSelectRow( - startIcon = { - WalletImage( - url = wallet.imageUrl, - modifier = Modifier - .size(40.dp) - .clip(RoundedCornerShape(10.dp)) - ) - }, - text = wallet.name, - contentPadding = PaddingValues(vertical = 4.dp), - onClick = { uriHandler.openPlayStore(wallet.playStore) } - ) - } - item { - ListSelectRow( - startIcon = { AllWalletsIcon() }, - text = "Explore all", - contentPadding = PaddingValues(vertical = 4.dp), - label = { ExternalIcon() }, - onClick = { uriHandler.openUri("https://explorer.walletconnect.com/?type=wallet") } - ) - } - } -} - -@UiModePreview -@Composable -private fun PreviewGetAWallet() { - Web3ModalPreview(title = "Get a Wallet") { - GetAWalletContent(wallets = testWallets) - } -} - diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectState.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectState.kt deleted file mode 100644 index 5c219408f..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/redirect/RedirectState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.redirect - -sealed class RedirectState { - object Loading: RedirectState() - object Reject: RedirectState() - object Expired: RedirectState() - object NotDetected: RedirectState() -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/scan_code/ScanCodeRoute.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/scan_code/ScanCodeRoute.kt deleted file mode 100644 index c94b9fc54..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/scan_code/ScanCodeRoute.kt +++ /dev/null @@ -1,170 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.scan_code - -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.ClipboardManager -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.walletconnect.android.pulse.model.ConnectionMethod -import com.walletconnect.modal.ui.components.qr.QrCodeType -import com.walletconnect.modal.ui.components.qr.WalletConnectQRCode -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.domain.delegate.Web3ModalDelegate -import com.walletconnect.web3.modal.ui.components.internal.OrientationBox -import com.walletconnect.web3.modal.ui.components.internal.commons.entry.CopyActionEntry -import com.walletconnect.web3.modal.ui.components.internal.snackbar.LocalSnackBarHandler -import com.walletconnect.web3.modal.ui.previews.Landscape -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.routes.connect.ConnectViewModel -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme -import kotlinx.coroutines.flow.filter - -@Composable -internal fun ScanQRCodeRoute(connectViewModel: ConnectViewModel) { - val snackBarHandler = LocalSnackBarHandler.current - val clipboardManager: ClipboardManager = LocalClipboardManager.current - var uri by remember { mutableStateOf(connectViewModel.uri) } - - LaunchedEffect(Unit) { - Web3ModalDelegate - .wcEventModels - .filter { event -> event is Modal.Model.RejectedSession || event is Modal.Model.SessionAuthenticateResponse.Error } - .collect { - snackBarHandler.showErrorSnack("Declined") - connectViewModel.connectWalletConnect(name = "WalletConnect", method = ConnectionMethod.QR_CODE, linkMode = null) { newUri -> uri = newUri } - } - } - - ScanQRCodeContent( - uri = uri, - onCopyLinkClick = { - snackBarHandler.showSuccessSnack("Link copied") - clipboardManager.setText(AnnotatedString(connectViewModel.uri)) - } - ) -} - -@Composable -private fun ScanQRCodeContent( - uri: String, onCopyLinkClick: () -> Unit -) { - OrientationBox( - portrait = { PortraitContent(uri, onCopyLinkClick) }, - landscape = { LandscapeContent(uri, onCopyLinkClick) } - ) - -} - -@Composable -private fun LandscapeContent( - uri: String, - onCopyLinkClick: () -> Unit -) { - Row( - modifier = Modifier - .fillMaxSize() - .padding(vertical = 5.dp, horizontal = 10.dp), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .weight(1f) - .padding(vertical = 5.dp), - contentAlignment = Alignment.Center - ) { - QRCode(uri = uri) - } - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { - ScanQrCodeLabel() - Spacer(modifier = Modifier.height(12.dp)) - CopyActionEntry(onClick = onCopyLinkClick) - } - } -} - -@Composable -private fun PortraitContent( - uri: String, - onCopyLinkClick: () -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(20.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - QRCode(uri = uri) - Spacer(modifier = Modifier.height(20.dp)) - ScanQrCodeLabel() - Spacer(modifier = Modifier.height(12.dp)) - CopyActionEntry(onClick = onCopyLinkClick) - } -} - -@Composable -private fun ScanQrCodeLabel() { - Text( - text = "Scan this QR code with your phone", - modifier = Modifier.fillMaxWidth(), style = Web3ModalTheme.typo.paragraph400, textAlign = TextAlign.Center - ) -} - -@Composable -private fun QRCode(uri: String) { - if (isSystemInDarkTheme()) { - Box( - modifier = Modifier - .background(Web3ModalTheme.colors.inverse100, shape = RoundedCornerShape(36.dp)) - .padding(16.dp) - ) { - WalletConnectQRCode( - qrData = uri, - primaryColor = Web3ModalTheme.colors.inverse000, - logoColor = Web3ModalTheme.colors.accent100, - type = QrCodeType.W3M - ) - } - } else { - WalletConnectQRCode( - qrData = uri, - primaryColor = Web3ModalTheme.colors.inverse000, - logoColor = Web3ModalTheme.colors.accent100, - type = QrCodeType.W3M - ) - } -} - -@UiModePreview -@Landscape -@Composable -private fun ScanQRCodePreview() { - Web3ModalPreview("Mobile Wallets") { - ScanQRCodeContent("47442c19ea7c6a7a836fa3e53af1ddd375438daaeea9acdbf595e989a731b73249a10a7cc0e343ca627e536609", {}) - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/what_is_wallet/WhatIsWallet.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/what_is_wallet/WhatIsWallet.kt deleted file mode 100644 index 0ae450076..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/routes/connect/what_is_wallet/WhatIsWallet.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect.what_is_wallet - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pulse.domain.SendEventInterface -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.web3.modal.R -import com.walletconnect.web3.modal.ui.components.internal.commons.HelpSection -import com.walletconnect.web3.modal.ui.components.internal.commons.VerticalSpacer -import com.walletconnect.web3.modal.ui.components.internal.commons.WalletIcon -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonSize -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ButtonStyle -import com.walletconnect.web3.modal.ui.components.internal.commons.button.ImageButton -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.UiModePreview -import com.walletconnect.web3.modal.ui.previews.Web3ModalPreview -import com.walletconnect.web3.modal.ui.theme.Web3ModalTheme - -@Composable -internal fun WhatIsWallet( - navController: NavController -) { - val sendEventUseCase: SendEventInterface = wcKoinApp.koin.get() - WhatIsWallet { - sendEventUseCase.send(Props(EventType.TRACK, EventType.Track.CLICK_GET_WALLET)) - navController.navigate(Route.GET_A_WALLET.path) - } -} - -@Composable -private fun WhatIsWallet( - onGetAWalletClick: () -> Unit -) { - Column( - modifier = Modifier - .padding(horizontal = 20.dp) - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally - ) { - VerticalSpacer(20.dp) - HelpSection( - title = "One login for all of web3", - body = "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", - assets = listOf(R.drawable.login, R.drawable.profile, R.drawable.lock) - ) - VerticalSpacer(24.dp) - HelpSection( - title = "A home for your digital assets", - body = "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", - assets = listOf(R.drawable.defi, R.drawable.nft, R.drawable.eth) - ) - VerticalSpacer(24.dp) - HelpSection( - title = "Your gateway to a new web", - body = "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", - assets = listOf(R.drawable.browser, R.drawable.noun, R.drawable.dao) - ) - VerticalSpacer(20.dp) - ImageButton( - text = "Get a wallet", - image = { WalletIcon(Web3ModalTheme.colors.inverse100) }, - style = ButtonStyle.MAIN, - size = ButtonSize.S, - paddingValues = PaddingValues(start = 8.dp, top = 6.dp, end = 12.dp, 6.dp), - onClick = { onGetAWalletClick() } - ) - VerticalSpacer(30.dp) - } -} - -@Composable -@UiModePreview -private fun HelpContentPreview() { - Web3ModalPreview("What is a Wallet?") { - WhatIsWallet {} - } -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/CustomComposition.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/CustomComposition.kt deleted file mode 100644 index d540be972..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/CustomComposition.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.web3.modal.ui.theme - -import androidx.compose.runtime.compositionLocalOf -import com.walletconnect.web3.modal.ui.Web3ModalTheme - -internal data class CustomComposition( - val mode: Web3ModalTheme.Mode = Web3ModalTheme.Mode.AUTO, - val lightColors: Web3ModalTheme.Colors = Web3ModalTheme.provideLightWeb3ModalColors(), - val darkColors: Web3ModalTheme.Colors = Web3ModalTheme.provideDarkWeb3ModalColor(), -) - -internal val LocalCustomComposition = compositionLocalOf { CustomComposition() } \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Theme.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Theme.kt deleted file mode 100644 index 5e77dd895..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Theme.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.web3.modal.ui.theme - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.compositionLocalOf - -@Composable -internal fun ProvideWeb3ModalThemeComposition( - content: @Composable () -> Unit, -) { - val composition = LocalCustomComposition.current - val colors = provideWeb3ModalColors(composition) - val typography = provideDefaultTypography(colors) - CompositionLocalProvider( - LocalColorsComposition provides colors, - LocalTypographyComposition provides typography, - content = content - ) -} - -internal object Web3ModalTheme { - val colors: Web3ModalColors - @Composable - get() = LocalColorsComposition.current - - val typo: Web3ModalTypography - @Composable - get() = LocalTypographyComposition.current -} - -private val LocalTypographyComposition = compositionLocalOf { - error("No typography provided") -} - -private val LocalColorsComposition = compositionLocalOf { - error("No colors provided") -} diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Web3ModalColors.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Web3ModalColors.kt deleted file mode 100644 index 66b78b5a5..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/theme/Web3ModalColors.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.walletconnect.web3.modal.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import com.walletconnect.web3.modal.ui.Web3ModalTheme - -internal data class Web3ModalColors( - val accent100: Color, - val accent90: Color, - val accent80: Color, - val foreground: ColorPalette, - val background: ColorPalette, - val grayGlass: Color, - val success: Color, - val error: Color, - val teal: Color, - val magenta: Color, - val indigo: Color, - val orange: Color, - val purple: Color -) { - val inverse100 = Color.White - val inverse000 = Color.Black - - val success15 = success.copy(.15f) - - val accent20: Color = accent100.copy(.2f) - val accent15: Color = accent100.copy(.15f) - val accent10: Color = accent100.copy(.1f) - - val grayGlass02: Color = grayGlass.copy(.02f) - val grayGlass05: Color = grayGlass.copy(.05f) - val grayGlass10: Color = grayGlass.copy(.10f) - val grayGlass15: Color = grayGlass.copy(.15f) - val grayGlass20: Color = grayGlass.copy(.2f) - val grayGlass25: Color = grayGlass.copy(.25f) - val grayGlass30: Color = grayGlass.copy(.3f) -} -data class ColorPalette( - val color100: Color, - val color125: Color, - val color150: Color, - val color175: Color, - val color200: Color, - val color225: Color, - val color250: Color, - val color275: Color, - val color300: Color, -) -@Composable -internal fun provideWeb3ModalColors(composition: CustomComposition): Web3ModalColors = if (composition.mode.isDarkTheme()) { - Web3ModalColors( - accent100 = composition.darkColors.accent100, - accent90 = composition.darkColors.accent90, - accent80 = composition.darkColors.accent80, - foreground = composition.darkColors.foreground, - background = composition.darkColors.background, - grayGlass = composition.darkColors.grayGlass, - success = composition.darkColors.success, - error = composition.darkColors.error, - teal = defaultDarkWeb3ModalColors.teal, - magenta = defaultDarkWeb3ModalColors.magenta, - indigo = defaultDarkWeb3ModalColors.indigo, - orange = defaultDarkWeb3ModalColors.orange, - purple = defaultDarkWeb3ModalColors.purple - ) -} else { - Web3ModalColors( - accent100 = composition.lightColors.accent100, - accent90 = composition.lightColors.accent90, - accent80 = composition.lightColors.accent80, - foreground = composition.lightColors.foreground, - background = composition.lightColors.background, - grayGlass = composition.lightColors.grayGlass, - success = composition.lightColors.success, - error = composition.lightColors.error, - teal = defaultLightWeb3ModalColors.teal, - magenta = defaultLightWeb3ModalColors.magenta, - indigo = defaultLightWeb3ModalColors.indigo, - orange = defaultLightWeb3ModalColors.orange, - purple = defaultLightWeb3ModalColors.purple - ) -} - -@Composable -private fun Web3ModalTheme.Mode.isDarkTheme() = when(this) { - Web3ModalTheme.Mode.LIGHT -> false - Web3ModalTheme.Mode.DARK -> true - Web3ModalTheme.Mode.AUTO -> isSystemInDarkTheme() -} - -internal val defaultDarkWeb3ModalColors = Web3ModalColors( - accent100 = Color(0xFF47A1FF), - accent90 = Color(0xFF59AAFF), - accent80 = Color(0xFF6CB4FF), - foreground = ColorPalette( - Color(0xFFE4E7E7), - Color(0xFFD0D5D5), - Color(0xFFA8B1B1), - Color(0xFFA8B0B0), - Color(0xFF949E9E), - Color(0xFF868F8F), - Color(0xFF788080), - Color(0xFF788181), - Color(0xFF637777) - ), - background = ColorPalette( - Color(0xFF141414), - Color(0xFF191A1A), - Color(0xFF1E1F1F), - Color(0xFF222525), - Color(0xFF272A2A), - Color(0xFF2C3030), - Color(0xFF313535), - Color(0xFF363B3B), - Color(0xFF3B4040) - ), - grayGlass = Color(0xFFFFFFFF), - success = Color(0xFF26D962), - error = Color(0xFFF25A67), - teal = Color(0xFF36E2E2), - magenta = Color(0xFFCB4D8C), - indigo = Color(0xFF516DFB), - orange = Color(0xFFFFA64C), - purple = Color(0xFF9063F7), -) - -internal val defaultLightWeb3ModalColors = Web3ModalColors( - accent100 = Color(0xFF3396FF), - accent90 = Color(0xFF2D7DD2), - accent80 = Color(0xFF2978CC), - foreground = ColorPalette( - Color(0xFF141414), - Color(0xFF2D3131), - Color(0xFF474D4D), - Color(0xFF636D6D), - Color(0xFF798686), - Color(0xFF828F8F), - Color(0xFF8B9797), - Color(0xFF95A0A0), - Color(0xFF9EA9A9) - ), - background = ColorPalette( - Color(0xFFFFFFFF), - Color(0xFFF5FAFA), - Color(0xFFF3F8F8), - Color(0xFFEEF4F4), - Color(0xFFEAF1F1), - Color(0xFFE5EDED), - Color(0xFFE1E9E9), - Color(0xFFDCE7E7), - Color(0xFFD8E3E3) - ), - grayGlass = Color(0xFF000000), - success = Color(0xFF26B562), - error = Color(0xFFF05142), - teal = Color(0xFF2BB6B6), - magenta = Color(0xFFC65380), - indigo = Color(0xFF3D5CF5), - orange = Color(0xFFEA8C2E), - purple = Color(0xFF794CFF), -) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Navigation.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Navigation.kt deleted file mode 100644 index 461ad3f4d..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/ui/utils/Navigation.kt +++ /dev/null @@ -1,60 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.web3.modal.ui.utils - -import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.animateContentSize -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.VisibilityThreshold -import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import com.google.accompanist.navigation.animation.AnimatedNavHost -import com.google.accompanist.navigation.animation.composable - -@Composable -internal fun AnimatedNavGraph( - navController: NavHostController, - startDestination: String, - builder: NavGraphBuilder.() -> Unit -) { - val stiffnessAnimSpec = spring(stiffness = Spring.StiffnessLow, visibilityThreshold = IntOffset.VisibilityThreshold) - val tweenAnimSpec = tween(durationMillis = 400) - - AnimatedNavHost( - navController = navController, - startDestination = startDestination, - contentAlignment = Alignment.BottomCenter, - enterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeIn(animationSpec = tweenAnimSpec) }, - exitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) + fadeOut(animationSpec = tweenAnimSpec) }, - popEnterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) + fadeIn(animationSpec = tweenAnimSpec) }, - popExitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeOut(animationSpec = tweenAnimSpec) }, - modifier = Modifier.animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMedium, visibilityThreshold = IntSize.VisibilityThreshold)), - builder = builder - ) -} - -internal fun NavGraphBuilder.animatedComposable(route: String, content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit) { - val stiffnessAnimSpec = spring(stiffness = Spring.StiffnessLow, visibilityThreshold = IntOffset.VisibilityThreshold) - val tweenAnimSpec = tween(durationMillis = 400) - composable( - route = route, - content = content, - enterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeIn(animationSpec = tweenAnimSpec) }, - exitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) + fadeOut(animationSpec = tweenAnimSpec) }, - popEnterTransition = { slideInHorizontally(animationSpec = stiffnessAnimSpec) + fadeIn(animationSpec = tweenAnimSpec) }, - popExitTransition = { slideOutHorizontally(animationSpec = stiffnessAnimSpec) { it / 2 } + fadeOut(animationSpec = tweenAnimSpec) }, - ) -} \ No newline at end of file diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/Image.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/Image.kt deleted file mode 100644 index 2fa1dd1d6..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/Image.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.walletconnect.web3.modal.utils - -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.ColorMatrix -import coil.request.ImageRequest -import com.walletconnect.android.BuildConfig -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.wcKoinApp - -internal fun ImageRequest.Builder.imageHeaders() = apply { - addHeader("x-project-id", wcKoinApp.koin.get().value) - addHeader("x-sdk-version", BuildConfig.SDK_VERSION) - addHeader("x-sdk-type", "w3m") -} - -internal val grayColorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }) diff --git a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ViewUtils.kt b/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ViewUtils.kt deleted file mode 100644 index 0e0e1a291..000000000 --- a/product/web3modal/src/main/kotlin/com/walletconnect/web3/modal/utils/ViewUtils.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.web3.modal.utils - -import com.walletconnect.web3.modal.ui.components.button.AccountButtonType -import com.walletconnect.web3.modal.ui.components.button.ConnectButtonSize - -internal fun Int.toConnectButtonSize() = when (this) { - 1 -> ConnectButtonSize.SMALL - else -> ConnectButtonSize.NORMAL -} - -internal fun Int.toAccountButtonType() = when(this) { - 1 -> AccountButtonType.MIXED - else -> AccountButtonType.NORMAL -} diff --git a/product/web3modal/src/main/res/drawable/browser.xml b/product/web3modal/src/main/res/drawable/browser.xml deleted file mode 100644 index a4661bcb0..000000000 --- a/product/web3modal/src/main/res/drawable/browser.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/dao.xml b/product/web3modal/src/main/res/drawable/dao.xml deleted file mode 100644 index 913a02fe6..000000000 --- a/product/web3modal/src/main/res/drawable/dao.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/defi.xml b/product/web3modal/src/main/res/drawable/defi.xml deleted file mode 100644 index a4bbed9f8..000000000 --- a/product/web3modal/src/main/res/drawable/defi.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/eth.xml b/product/web3modal/src/main/res/drawable/eth.xml deleted file mode 100644 index 0399df475..000000000 --- a/product/web3modal/src/main/res/drawable/eth.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/ic_scan.xml b/product/web3modal/src/main/res/drawable/ic_scan.xml deleted file mode 100644 index 89f87391d..000000000 --- a/product/web3modal/src/main/res/drawable/ic_scan.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/ic_wallet_connect_logo.xml b/product/web3modal/src/main/res/drawable/ic_wallet_connect_logo.xml deleted file mode 100644 index 22a58f0dd..000000000 --- a/product/web3modal/src/main/res/drawable/ic_wallet_connect_logo.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/product/web3modal/src/main/res/drawable/ic_wallet_connect_qr_logo.xml b/product/web3modal/src/main/res/drawable/ic_wallet_connect_qr_logo.xml deleted file mode 100644 index 4675f0c6a..000000000 --- a/product/web3modal/src/main/res/drawable/ic_wallet_connect_qr_logo.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/product/web3modal/src/main/res/drawable/lock.xml b/product/web3modal/src/main/res/drawable/lock.xml deleted file mode 100644 index 4fe43c0ac..000000000 --- a/product/web3modal/src/main/res/drawable/lock.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/login.xml b/product/web3modal/src/main/res/drawable/login.xml deleted file mode 100644 index ad2d723c9..000000000 --- a/product/web3modal/src/main/res/drawable/login.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/nft.xml b/product/web3modal/src/main/res/drawable/nft.xml deleted file mode 100644 index 8844c1053..000000000 --- a/product/web3modal/src/main/res/drawable/nft.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/noun.xml b/product/web3modal/src/main/res/drawable/noun.xml deleted file mode 100644 index f226c9fd3..000000000 --- a/product/web3modal/src/main/res/drawable/noun.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - diff --git a/product/web3modal/src/main/res/drawable/profile.xml b/product/web3modal/src/main/res/drawable/profile.xml deleted file mode 100644 index aee62b906..000000000 --- a/product/web3modal/src/main/res/drawable/profile.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChainSwitchRedirectTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChainSwitchRedirectTest.kt deleted file mode 100644 index 7df8faedf..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChainSwitchRedirectTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account - -import com.android.resources.NightMode -import com.walletconnect.web3.modal.presets.Web3ModalChainsPresets -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.account.chain_redirect.ChainRedirectState -import com.walletconnect.web3.modal.ui.routes.account.chain_redirect.ChainSwitchRedirectScreen -import com.walletconnect.web3.modal.utils.ScreenShotTest -import org.junit.Ignore -import org.junit.Test - -@Ignore("This test is not working on CI for Sonar only") -internal class ChainSwitchRedirectTest : ScreenShotTest("account/" + Route.CHAIN_SWITCH_REDIRECT.path) { - - private val chain = Web3ModalChainsPresets.ethChains["1"]!! - - @Test - fun `test ChainSwitchRedirect with Loading in LightMode`() = runRouteScreenShotTest( - title = chain.chainName - ) { - ChainSwitchRedirectScreen(chain, ChainRedirectState.Loading, {}) - } - - @Test - fun `test ChainSwitchRedirect with Loading in DarkMode`() = runRouteScreenShotTest( - title = chain.chainName, - nightMode = NightMode.NIGHT - ) { - ChainSwitchRedirectScreen(chain, ChainRedirectState.Loading, {}) - } - - @Test - fun `test ChainSwitchRedirect with Decline in LightMode`() = runRouteScreenShotTest( - title = chain.chainName - ) { - ChainSwitchRedirectScreen(chain, ChainRedirectState.Declined, {}) - } - - @Test - fun `test ChainSwitchRedirect with Decline in DarkMode`() = runRouteScreenShotTest( - title = chain.chainName, - nightMode = NightMode.NIGHT - ) { - ChainSwitchRedirectScreen(chain, ChainRedirectState.Declined, {}) - } -} \ No newline at end of file diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChangeNetworkRouteTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChangeNetworkRouteTest.kt deleted file mode 100644 index 3d4a73b5b..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/account/ChangeNetworkRouteTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.account - -import com.android.resources.NightMode -import com.android.resources.ScreenOrientation -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.domain.model.AccountData -import com.walletconnect.web3.modal.domain.usecase.GetSelectedChainUseCase -import com.walletconnect.web3.modal.presets.Web3ModalChainsPresets -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.account.change_network.ChangeNetworkRoute -import com.walletconnect.web3.modal.utils.ScreenShotTest -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.flow.StateFlow -import org.junit.Before -import org.junit.Ignore -import org.junit.Test -import org.koin.dsl.module - -@Ignore("This test is not working on CI for Sonar only") -internal class ChangeNetworkRouteTest: ScreenShotTest("account/${Route.CHANGE_NETWORK.path}") { - - private val viewModel: AccountViewModel = mockk() - private val selectedChain: StateFlow = mockk() - private val uiState: StateFlow> = mockk() - private val getSelectedChainUseCase: GetSelectedChainUseCase = mockk() - - @Before - fun setup() { - Web3Modal.chains = Web3ModalChainsPresets.ethChains.values.toList() - every { viewModel.accountState } returns uiState - every { uiState.value } returns UiState.Success(AccountData("0x2765d421FB91182490D602E671a", Web3ModalChainsPresets.ethChains.values.toList())) - every { viewModel.selectedChain } returns selectedChain - every { selectedChain.value } returns Web3ModalChainsPresets.ethChains["1"]!! - every { getSelectedChainUseCase() } returns "1" - every { viewModel.getSelectedChainOrFirst() } returns Web3ModalChainsPresets.ethChains.getOrElse("1") { throw IllegalStateException("Chain not found") } - wcKoinApp.koin.loadModules(modules = listOf(module { single { getSelectedChainUseCase } })) - - } - - @Test - fun `test ChangeNetworkRoute in LightMode`() = runRouteScreenShotTest( - title = Route.CHANGE_NETWORK.title - ) { - ChangeNetworkRoute(accountViewModel = viewModel) - } - - @Test - fun `test ChangeNetworkRoute in DarkMode`() = runRouteScreenShotTest( - title = Route.CHANGE_NETWORK.title, - nightMode = NightMode.NIGHT - ) { - ChangeNetworkRoute(accountViewModel = viewModel) - } - - @Test - fun `test ChangeNetworkRoute in Landscape`() = runRouteScreenShotTest( - title = Route.CHANGE_NETWORK.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - ChangeNetworkRoute(accountViewModel = viewModel) - } -} \ No newline at end of file diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/AllWalletsRouteTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/AllWalletsRouteTest.kt deleted file mode 100644 index da02864cd..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/AllWalletsRouteTest.kt +++ /dev/null @@ -1,167 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import com.android.resources.NightMode -import com.android.resources.ScreenOrientation -import com.walletconnect.modal.ui.model.LoadingState -import com.walletconnect.util.Empty -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.all_wallets.AllWalletsRoute -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.flow.StateFlow -import org.junit.Before -import org.junit.Ignore -import org.junit.Rule -import org.junit.Test - -@Ignore("This test is not working on CI for Sonar only") -internal class AllWalletsRouteTest : ScreenShotTest("connect/${Route.ALL_WALLETS.path}") { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val viewModel: ConnectViewModel = mockk() - private val walletsState: StateFlow = mockk() - - @Before - fun setup() { - every { viewModel.walletsState } returns walletsState - every { viewModel.searchPhrase } returns String.Empty - } - - @Test - fun `test AllWalletsRoute with LoadingRefresh in LightMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title - ) { - every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with LoadingRefresh in DarkMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - nightMode = NightMode.NIGHT - ) { - every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with LoadingRefresh in Landscape`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - every { viewModel.walletsState.value } returns WalletsData(loadingState = LoadingState.REFRESH) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with EmptyList in LightMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title - ) { - every { viewModel.walletsState.value } returns WalletsData() - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with EmptyList in DarkMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - nightMode = NightMode.NIGHT - ) { - every { viewModel.walletsState.value } returns WalletsData() - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with EmptyList in Landscape`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - every { viewModel.walletsState.value } returns WalletsData() - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loaded Wallets in LightMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loaded Wallets in DarkMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - nightMode = NightMode.NIGHT - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loaded Wallets in Landscape`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loading Append in LightMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loading Append in DarkMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - nightMode = NightMode.NIGHT - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Loading Append in Landscape`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets, LoadingState.APPEND) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Search in LightMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title - ) { - every { viewModel.searchPhrase } returns "Meta" - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Search in DarkMode`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - nightMode = NightMode.NIGHT - ) { - every { viewModel.searchPhrase } returns "Meta" - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) - AllWalletsRoute(connectViewModel = viewModel) - } - - @Test - fun `test AllWalletsRoute with Search in Landscape`() = runRouteScreenShotTest( - title = Route.ALL_WALLETS.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - every { viewModel.searchPhrase } returns "Meta" - every { viewModel.walletsState.value } returns WalletsData(wallets = testWallets.filter { it.name.contains("Meta") }) - AllWalletsRoute(connectViewModel = viewModel) - } -} \ No newline at end of file diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectYourWalletRouteTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectYourWalletRouteTest.kt deleted file mode 100644 index ce68d58c2..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ConnectYourWalletRouteTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import com.android.resources.NightMode -import com.walletconnect.android.internal.common.modal.data.model.Wallet -import com.walletconnect.modal.ui.model.UiState -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.connect_wallet.ConnectWalletRoute -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.flow.StateFlow -import org.junit.Before -import org.junit.Ignore -import org.junit.Rule -import org.junit.Test - -@Ignore("This test is not working on CI for Sonar only") -internal class ConnectYourWalletRouteTest : ScreenShotTest("connect/${Route.CONNECT_YOUR_WALLET.path}") { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val viewModel: ConnectViewModel = mockk() - private val uiState: StateFlow>> = mockk() - - @Before - fun setup() { - every { viewModel.uiState } returns uiState - } - - @Test - fun `test ConnectYourWallet with Loading UiState in LightMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title - ) { - every { uiState.value } returns UiState.Loading() - ConnectWalletRoute(connectViewModel = viewModel) - } - - @Test - fun `test ConnectYourWallet with Loading UiState in DarkMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title, - nightMode = NightMode.NIGHT - ) { - every { uiState.value } returns UiState.Loading() - ConnectWalletRoute(connectViewModel = viewModel) - } - - @Test - fun `test ConnectYourWallet with Error UiState in LightMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title - ) { - every { uiState.value } returns UiState.Error(Throwable("Connection error")) - ConnectWalletRoute(connectViewModel = viewModel) - } - - @Test - fun `test ConnectYourWallet with Error UiState in DarkMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title, - nightMode = NightMode.NIGHT - ) { - every { uiState.value } returns UiState.Error(Throwable("Connection error")) - ConnectWalletRoute(connectViewModel = viewModel) - } - - @Test - fun `test ConnectYourWallet with Success UiState in LightMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title - ) { - every { uiState.value } returns UiState.Success(testWallets) - every { viewModel.getWalletsTotalCount() } returns testWallets.size - ConnectWalletRoute(connectViewModel = viewModel) - } - - @Test - fun `test ConnectYourWallet with Success UiState in DarkMode`() = runRouteScreenShotTest( - title = Route.CONNECT_YOUR_WALLET.title, - nightMode = NightMode.NIGHT - ) { - every { uiState.value } returns UiState.Success(testWallets) - every { viewModel.getWalletsTotalCount() } returns testWallets.size - ConnectWalletRoute(connectViewModel = viewModel) - } -} \ No newline at end of file diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/GetAWalletRouteTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/GetAWalletRouteTest.kt deleted file mode 100644 index 04c746b14..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/GetAWalletRouteTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import com.android.resources.NightMode -import com.android.resources.ScreenOrientation -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.previews.testWallets -import com.walletconnect.web3.modal.ui.routes.connect.get_wallet.GetAWalletRoute -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest -import org.junit.Ignore -import org.junit.Rule -import org.junit.Test - -@Ignore("This test is not working on CI for Sonar only") -internal class GetAWalletRouteTest: ScreenShotTest("connect/${Route.GET_A_WALLET.path}") { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - @Test - fun `test GetAWalletRoute in LightMode`() = runRouteScreenShotTest( - title = Route.GET_A_WALLET.title - ) { - GetAWalletRoute(testWallets) - } - - @Test - fun `test GetAWalletRoute in DarkMode`() = runRouteScreenShotTest( - title = Route.GET_A_WALLET.title, - nightMode = NightMode.NIGHT - ) { - GetAWalletRoute(testWallets) - } - - @Test - fun `test GetAWalletRoute in Landscape`() = runRouteScreenShotTest( - title = Route.GET_A_WALLET.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - GetAWalletRoute(testWallets) - } -} diff --git a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ScanCodeRouteTest.kt b/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ScanCodeRouteTest.kt deleted file mode 100644 index 3f7328a74..000000000 --- a/product/web3modal/src/test/kotlin/com/walletconnect/web3/modal/ui/routes/connect/ScanCodeRouteTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.walletconnect.web3.modal.ui.routes.connect - -import com.android.resources.NightMode -import com.android.resources.ScreenOrientation -import com.walletconnect.web3.modal.ui.navigation.Route -import com.walletconnect.web3.modal.ui.routes.connect.scan_code.ScanQRCodeRoute -import com.walletconnect.web3.modal.utils.MainDispatcherRule -import com.walletconnect.web3.modal.utils.ScreenShotTest -import io.mockk.every -import io.mockk.mockk -import org.junit.Before -import org.junit.Ignore -import org.junit.Rule -import org.junit.Test - -@Ignore("This test is not working on CI for Sonar only") -internal class ScanCodeRouteTest: ScreenShotTest("connect/${Route.QR_CODE.path}") { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val viewModel: ConnectViewModel = mockk() - - @Before - fun setup() { - every { viewModel.uri } returns "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - } - - @Test - fun `test ScanCodeRoute in LightMode`() = runRouteScreenShotTest( - title = Route.QR_CODE.title - ) { - ScanQRCodeRoute(connectViewModel = viewModel) - } - - @Test - fun `test ScanCodeRoute in DarkMode`() = runRouteScreenShotTest( - title = Route.QR_CODE.title, - nightMode = NightMode.NIGHT - ) { - ScanQRCodeRoute(connectViewModel = viewModel) - } - - @Test - fun `test ScanCodeRoute in Landscape`() = runRouteScreenShotTest( - title = Route.QR_CODE.title, - orientation = ScreenOrientation.LANDSCAPE - ) { - ScanQRCodeRoute(connectViewModel = viewModel) - } -} diff --git a/product/web3wallet/ReadMe.md b/product/web3wallet/ReadMe.md deleted file mode 100644 index ac782301f..000000000 --- a/product/web3wallet/ReadMe.md +++ /dev/null @@ -1,285 +0,0 @@ -# **WalletConnect Web3Wallet - Kotlin** - -Kotlin implementation of Web3Wallet for Android applications. - -![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/web3wallet) - -## Requirements - -* Android min SDK 23 -* Java 11 - -## Documentation and usage -* [Web3Wallet installation](https://docs.walletconnect.com/2.0/kotlin/web3wallet/installation) -* [Web3Wallet usage](https://docs.walletconnect.com/2.0/kotlin/web3wallet/wallet-usage) - -## Installation - -root/build.gradle.kts: - -```gradle -allprojects { - repositories { - mavenCentral() - maven { url "https://jitpack.io" } - } -} -``` - -app/build.gradle.kts - -```gradle -implementation(platform("com.walletconnect:android-bom:{BOM version}")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:web3wallet") -``` - -  - -## Migration guide from SignClient and AuthClient to Web3Wallet - -Web3Wallet SDK introduces a new interface for all wallets that wraps the Sign and Auth clients internally. Only one dependency is needed to -enable new use cases for wallets. Down below you can find a migration guide for Sign and Auth clients. - -## Migration from SignClient - -### Initialization - -```kotlin -//CoreClient -CoreClient.initialize(relayServerUrl, connectionType, application, metaData) { error -> } - -//SignClient -val initParams = Sign.Params.Init(CoreClient) -SignClient.initialize(initParams) { error -> } - -//Web3Wallet -val initParams = Wallet.Params.Init(CoreClient) -Web3Wallet.initialize(initParams) { error -> } -``` - -### Pair with a Dapp - -```kotlin -//CoreClient -val pairingParams = Core.Params.Pair(pairingUri) -CoreClient.Pairing.pair(pairingParams) { error -> } - -//Web3Wallet -val pairingParams = Wallet.Params.Pair(pairingUri) -Web3Wallet.pair(pairingParams) { error -> } -``` - -### Approve a session - -```kotlin -//SignClient -val approveParams: Sign.Params.Approve = Sign.Params.Approve(proposerPublicKey, namespaces) -SignClient.approveSession(approveParams) { error -> } - -//Web3Wallet -val approveParams = Wallet.Params.SessionApprove(proposerPublicKey namespaces) -Web3Wallet.approveSession(approveProposal) { error -> } -``` - -### Reject a session - -```kotlin -//SignClient -val rejectParams = Sign.Params.Reject = Reject(proposerPublicKey, rejectionReason, rejectionCode) -SignClient.rejectSession(rejectParams) { error -> } - -//Web3Wallet -val reject = Wallet.Params.SessionReject(proposerPublicKey, reason) -Web3Wallet.rejectSession(reject) { error -> } -``` - -### Respond to a session request - -```kotlin -//SignClient -val jsonRpcResponse = Sign.Model.JsonRpcResponse.JsonRpcResult(requestId, result) -val result = Sign.Params.Response(sessionTopic, jsonRpcResponse) -SignClient.respond(result) { error -> } - -//Web3Wallet -val jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcResult(requestId, result) -val response = Wallet.Params.SessionRequestResponse(sessionTopic, jsonRpcResponse) -Web3Wallet.respondSessionRequest(response) { error -> } -``` - -### Reject a session request - -```kotlin -//SignClient -val jsonRpcResponseError = Sign.Model.JsonRpcResponse.JsonRpcError(requestId, code, message) /*Session Request ID along with error code and message*/ -val result = Sign.Params.Response(sessionTopic, jsonRpcResponse) -SignClient.respond(result) { error -> } - -//Web3Wallet -val jsonRpcResponse = Wallet.Model.JsonRpcResponse.JsonRpcError(requestId, code, message) -val result = Wallet.Params.SessionRequestResponse(sessionTopic, jsonRpcResponse) -Web3Wallet.respondSessionRequest(result) { error -> } -``` - -### Update a session - -```kotlin -//SignClient -val updateParams = Sign.Params.Update(sessionTopic, namespaces) -SignClient.update(updateParams) { error -> } - -//Web3Wallet -val update = Wallet.Params.SessionUpdate(sessionTopic, namespaces) -Web3Wallet.updateSession(update) { error -> } -``` - -### Extend a session - -```kotlin -//SignClient -val extendParams = Sign.Params.Extend(sessionTopic) -WalletConnectClient.extend(exdendParams) { error -> } - -//Web3Wallet -val extend = Wallet.Params.SessionExtend(sessionTopic) -Web3Wallet.extendSession(extend) { error -> } -``` - -### Disconnect a session - -```kotlin -//SignClient -val disconnectParams = Sign.Params.Disconnect(sessionTopic, disconnectionReason, disconnectionCode) -SignClient.disconnect(disconnectParams) { error -> } - -//Web3Wallet -val disconnect = Wallet.Params.SessionDisconnect(sessionTopic) -Web3Wallet.disconnectSession(disconnect) { error -> } -``` - -### WalletDelegate - -```kotlin -//SignClient -val walletDelegate = object : SignClient.WalletDelegate { - override fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal) { - // Triggered when wallet receives the session proposal sent by a Dapp - } - - override fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest) { - // Triggered when a Dapp sends SessionRequest to sign a transaction or a message - } - - override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { - // Triggered when the session is deleted by the peer - } - - override fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) { - // Triggered when wallet receives the session settlement response from Dapp - } - - override fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) { - // Triggered when wallet receives the session update response from Dapp - } - - override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { - //Triggered whenever the connection state is changed - } - - override fun onError(error: Sign.Model.Error) { - // Triggered whenever there is an issue inside the SDK - } -} -SignClient.setWalletDelegate(walletDelegate) - -//Web3Wallet -val walletDelegate = object : Web3Wallet.WalletDelegate { - override fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal) { - // Triggered when wallet receives the session proposal sent by a Dapp - } - - override fun onSessionRequest(sessionRequest: Wallet.Model.SessionRequest) { - // Triggered when a Dapp sends SessionRequest to sign a transaction or a message - } - - override fun onAuthRequest(authRequest: Wallet.Model.AuthRequest) { - // Triggered when Dapp / Requester makes an authorisation request - } - - override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { - // Triggered when the session is deleted by the peer - } - - override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) { - // Triggered when wallet receives the session settlement response from Dapp - } - - override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) { - // Triggered when wallet receives the session update response from Dapp - } - - override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { - //Triggered whenever the connection state is changed - } - - override fun onError(error: Wallet.Model.Error) { - // Triggered whenever there is an issue inside the SDK - } -} -Web3Wallet.setWalletDelegate(walletDelegate) -``` - -## Migration from AuthClient - -### Initialization - -```kotlin -//CoreClient -CoreClient.initialize(relayServerUrl, connectionType, application, metaData) { error -> } - -//AuthClient -val initParams = Auth.Params.Init(CoreClient) -AuthClient.initialize(init = Auth.Params.Init(core = CoreClient)) { error -> } - -//Web3Wallet -val initParams = Wallet.Params.Init(core = CoreClient) -Web3Wallet.initialize(initParams) { error -> } -``` - -### Authentication - -```kotlin -//AuthClient -val signature = CacaoSigner.sign(message, privateKey, SignatureType.EIP191) -AuthClient.respond(Auth.Params.Respond.Result(requestId, signature, issuer)) { error -> } - -//Web3Wallet -val signature = CacaoSigner.sign(message, privateKey, SignatureType.EIP191) -Web3Wallet.respondAuthRequest(Wallet.Params.AuthRequestResponse(requestId, signature, issuer)) { error -> } -``` - -### Message formatting - -```kotlin -//AuthClient -val payloadParams: Auth.Params.PayloadParams = //PayloadParams received in the onAuthRequest callback -val issuer = //MUST be the same as send with the respond methods and follows: https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md -val formatMessage = Auth.Params.FormatMessage(payloadParamspayloadParams, issuer) -AuthClient.formatMessage(formatMessage) - -//Web3Wallet -val payloadParams: Wallet.Model.PayloadParams = //PayloadParams received in the onAuthRequest callback -val issuer = //MUST be the same as send with the respond methods and follows: https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md -val formatMessage = Wallet.Params.FormatMessage(Wallet.Params.FormatMessage(payloadParams, issuer)) -Web3Wallet.formatMessage(formatMessage) -``` - -Test against: - -* Live dapp - Sign - https://react-app.walletconnect.com -* Live dapp - Auth - https://react-auth-dapp.walletconnect.com/ - -## Sample app - -* For sample wallet run `web3wallet module` diff --git a/product/web3wallet/build.gradle.kts b/product/web3wallet/build.gradle.kts deleted file mode 100644 index 9ce9593b8..000000000 --- a/product/web3wallet/build.gradle.kts +++ /dev/null @@ -1,67 +0,0 @@ -plugins { - id("com.android.library") - id(libs.plugins.kotlin.android.get().pluginId) - alias(libs.plugins.google.ksp) - id("publish-module-android") - id("jacoco-report") -} - -project.apply { - extra[KEY_PUBLISH_ARTIFACT_ID] = WEB_3_WALLET - extra[KEY_PUBLISH_VERSION] = WEB_3_WALLET_VERSION - extra[KEY_SDK_NAME] = "web3wallet" -} - -android { - namespace = "com.walletconnect.web3.wallet" - compileSdk = COMPILE_SDK - - defaultConfig { - minSdk = MIN_SDK - - aarMetadata { - minCompileSdk = MIN_SDK - } - - buildConfigField(type = "String", name = "SDK_VERSION", value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"") - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro", "${projectDir}/web3wallet-rules.pro") - } - } - lint { - abortOnError = true - ignoreWarnings = true - warningsAsErrors = false - } - - compileOptions { - sourceCompatibility = jvmVersion - targetCompatibility = jvmVersion - } - kotlinOptions { - jvmTarget = jvmVersion.toString() - freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.time.ExperimentalTime" - } - - buildFeatures { - buildConfig = true - } -} - -dependencies { - implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.messaging) - - debugImplementation(project(":core:android")) - debugImplementation(project(":protocol:sign")) - debugImplementation(project(":protocol:auth")) - - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") - releaseImplementation("com.walletconnect:sign:$SIGN_VERSION") - releaseImplementation("com.walletconnect:auth:$AUTH_VERSION") -} \ No newline at end of file diff --git a/product/web3wallet/src/main/AndroidManifest.xml b/product/web3wallet/src/main/AndroidManifest.xml deleted file mode 100644 index e94e2a41a..000000000 --- a/product/web3wallet/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/ClientMapper.kt b/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/ClientMapper.kt deleted file mode 100644 index d2a4d6d95..000000000 --- a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/ClientMapper.kt +++ /dev/null @@ -1,361 +0,0 @@ -package com.walletconnect.web3.wallet.client - -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.auth.client.Auth -import com.walletconnect.sign.client.Sign - -@JvmSynthetic -internal fun Map.toSign(): Map = - mapValues { (_, namespace) -> - Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toWallet(): Map = - mapValues { (_, namespace) -> - Wallet.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toWalletProposalNamespaces(): Map = - mapValues { (_, namespace) -> - Wallet.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toSignProposalNamespaces(): Map = - mapValues { (_, namespace) -> - Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Wallet.Model.JsonRpcResponse.toSign(): Sign.Model.JsonRpcResponse = - when (this) { - is Wallet.Model.JsonRpcResponse.JsonRpcResult -> this.toSign() - is Wallet.Model.JsonRpcResponse.JsonRpcError -> this.toSign() - } - -@JvmSynthetic -internal fun Wallet.Model.JsonRpcResponse.JsonRpcResult.toSign(): Sign.Model.JsonRpcResponse.JsonRpcResult = - Sign.Model.JsonRpcResponse.JsonRpcResult(id, result) - -@JvmSynthetic -internal fun Wallet.Model.JsonRpcResponse.JsonRpcError.toSign(): Sign.Model.JsonRpcResponse.JsonRpcError = - Sign.Model.JsonRpcResponse.JsonRpcError(id, code, message) - -@JvmSynthetic -internal fun Wallet.Params.AuthRequestResponse.toAuth(): Auth.Params.Respond = when (this) { - is Wallet.Params.AuthRequestResponse.Result -> Auth.Params.Respond.Result(id, signature.toAuth(), issuer) - is Wallet.Params.AuthRequestResponse.Error -> Auth.Params.Respond.Error(id, code, message) -} - -@JvmSynthetic -internal fun Wallet.Model.Cacao.Signature.toAuth(): Auth.Model.Cacao.Signature = Auth.Model.Cacao.Signature(t, s, m) - -@JvmSynthetic -internal fun Wallet.Model.Cacao.Signature.toSign(): Sign.Model.Cacao.Signature = Sign.Model.Cacao.Signature(t, s, m) - -@JvmSynthetic -internal fun Wallet.Model.SessionEvent.toSign(): Sign.Model.SessionEvent = Sign.Model.SessionEvent(name, data) - -@JvmSynthetic -internal fun Wallet.Model.Event.toSign(): Sign.Model.Event = Sign.Model.Event(topic, name, data, chainId) - -@JvmSynthetic -internal fun Wallet.Model.PayloadParams.toAuth(): Auth.Model.PayloadParams = - Auth.Model.PayloadParams( - type = type, - chainId = chainId, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - iat = iat, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - ) - -@JvmSynthetic -internal fun Wallet.Model.PayloadAuthRequestParams.toSign(): Sign.Model.PayloadParams = - Sign.Model.PayloadParams( - type = type ?: CacaoType.CAIP222.header, - chains = chains, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat - ) - -@JvmSynthetic -internal fun Sign.Model.Session.toWallet(): Wallet.Model.Session = Wallet.Model.Session( - pairingTopic, topic, expiry, requiredNamespaces.toWalletProposalNamespaces(), optionalNamespaces?.toWalletProposalNamespaces(), namespaces.toWallet(), metaData -) - -@JvmSynthetic -internal fun List.mapToPendingRequests(): List = map { request -> - Wallet.Model.PendingSessionRequest( - request.requestId, - request.topic, - request.method, - request.chainId, - request.params - ) -} - -@JvmSynthetic -internal fun List.mapToPendingSessionRequests(): List = map { request -> - Wallet.Model.SessionRequest( - request.topic, - request.chainId, - request.peerMetaData, - Wallet.Model.SessionRequest.JSONRPCRequest(request.request.id, request.request.method, request.request.params) - ) -} - -internal fun Auth.Model.PayloadParams.toWallet(): Wallet.Model.PayloadParams = - Wallet.Model.PayloadParams( - type = type, - chainId = chainId, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - iat = iat, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - ) - -@JvmSynthetic -internal fun List.toWallet(): List = - map { request -> - Wallet.Model.PendingAuthRequest( - request.id, - request.pairingTopic, - request.payloadParams.toWallet() - ) - } - -@JvmSynthetic -internal fun Sign.Model.SessionProposal.toWallet(): Wallet.Model.SessionProposal = - Wallet.Model.SessionProposal( - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toWalletProposalNamespaces(), - optionalNamespaces.toWalletProposalNamespaces(), - properties, - proposerPublicKey, - relayProtocol, - relayData - ) - -@JvmSynthetic -internal fun Sign.Model.SessionAuthenticate.toWallet(): Wallet.Model.SessionAuthenticate = - Wallet.Model.SessionAuthenticate(id, topic, participant.toWallet(), payloadParams.toWallet()) - -@JvmSynthetic -internal fun Sign.Model.SessionAuthenticate.Participant.toWallet(): Wallet.Model.SessionAuthenticate.Participant = Wallet.Model.SessionAuthenticate.Participant(publicKey, metadata) - -@JvmSynthetic -internal fun Sign.Model.PayloadParams.toWallet(): Wallet.Model.PayloadAuthRequestParams = - Wallet.Model.PayloadAuthRequestParams( - chains = chains, - type = type ?: CacaoType.CAIP222.header, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat - ) - -internal fun Sign.Model.VerifyContext.toWallet(): Wallet.Model.VerifyContext = - Wallet.Model.VerifyContext(id, origin, this.validation.toWallet(), verifyUrl, isScam) - -internal fun Sign.Model.Validation.toWallet(): Wallet.Model.Validation = - when (this) { - Sign.Model.Validation.VALID -> Wallet.Model.Validation.VALID - Sign.Model.Validation.INVALID -> Wallet.Model.Validation.INVALID - Sign.Model.Validation.UNKNOWN -> Wallet.Model.Validation.UNKNOWN - } - -internal fun Auth.Model.Validation.toWallet(): Wallet.Model.Validation = - when (this) { - Auth.Model.Validation.VALID -> Wallet.Model.Validation.VALID - Auth.Model.Validation.INVALID -> Wallet.Model.Validation.INVALID - Auth.Model.Validation.UNKNOWN -> Wallet.Model.Validation.UNKNOWN - } - -@JvmSynthetic -internal fun Sign.Model.SessionRequest.toWallet(): Wallet.Model.SessionRequest = - Wallet.Model.SessionRequest( - topic = topic, - chainId = chainId, - peerMetaData = peerMetaData, - request = Wallet.Model.SessionRequest.JSONRPCRequest( - id = request.id, - method = request.method, - params = request.params - ) - ) - -@JvmSynthetic -internal fun Sign.Model.DeletedSession.toWallet(): Wallet.Model.SessionDelete = - when (this) { - is Sign.Model.DeletedSession.Success -> Wallet.Model.SessionDelete.Success(topic, reason) - is Sign.Model.DeletedSession.Error -> Wallet.Model.SessionDelete.Error(error) - } - -@JvmSynthetic -internal fun Sign.Model.SettledSessionResponse.toWallet(): Wallet.Model.SettledSessionResponse = - when (this) { - is Sign.Model.SettledSessionResponse.Result -> Wallet.Model.SettledSessionResponse.Result(session.toWallet()) - is Sign.Model.SettledSessionResponse.Error -> Wallet.Model.SettledSessionResponse.Error(errorMessage) - } - -@JvmSynthetic -internal fun Sign.Model.SessionUpdateResponse.toWallet(): Wallet.Model.SessionUpdateResponse = - when (this) { - is Sign.Model.SessionUpdateResponse.Result -> Wallet.Model.SessionUpdateResponse.Result(topic, namespaces.toWallet()) - is Sign.Model.SessionUpdateResponse.Error -> Wallet.Model.SessionUpdateResponse.Error(errorMessage) - } - -@JvmSynthetic -internal fun Sign.Model.ExpiredProposal.toWallet(): Wallet.Model.ExpiredProposal = Wallet.Model.ExpiredProposal(pairingTopic, proposerPublicKey) - -@JvmSynthetic -internal fun Sign.Model.ExpiredRequest.toWallet(): Wallet.Model.ExpiredRequest = Wallet.Model.ExpiredRequest(topic, id) - -@JvmSynthetic -internal fun Auth.Event.AuthRequest.toWallet(): Wallet.Model.AuthRequest = Wallet.Model.AuthRequest(id, pairingTopic, payloadParams.toWallet()) - -@JvmSynthetic -internal fun Auth.Event.VerifyContext.toWallet(): Wallet.Model.VerifyContext = Wallet.Model.VerifyContext(id, origin, this.validation.toWallet(), verifyUrl, isScam) - -@JvmSynthetic -internal fun Auth.Model.VerifyContext.toWallet(): Wallet.Model.VerifyContext = Wallet.Model.VerifyContext(id, origin, this.validation.toWallet(), verifyUrl, isScam) - -@JvmSynthetic -internal fun Wallet.Model.SessionProposal.toSign(): Sign.Model.SessionProposal = - Sign.Model.SessionProposal( - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toSignProposalNamespaces(), - optionalNamespaces.toSignProposalNamespaces(), - properties, - proposerPublicKey, - relayProtocol, - relayData - ) - -@JvmSynthetic -internal fun Sign.Model.Message.SessionProposal.toWallet(): Wallet.Model.Message.SessionProposal = - Wallet.Model.Message.SessionProposal( - id, - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toWalletProposalNamespaces(), - optionalNamespaces.toWalletProposalNamespaces(), - properties, - proposerPublicKey, - relayProtocol, - relayData - ) - -@JvmSynthetic -internal fun Sign.Model.Message.SessionRequest.toWallet(): Wallet.Model.Message.SessionRequest = - Wallet.Model.Message.SessionRequest( - topic, - chainId, - peerMetaData, - Wallet.Model.Message.SessionRequest.JSONRPCRequest(request.id, request.method, request.params) - ) - -@JvmSynthetic -internal fun Auth.Model.Message.AuthRequest.toWallet(): Wallet.Model.Message.AuthRequest = with(payloadParams) { - Wallet.Model.Message.AuthRequest( - id, - pairingTopic, - metadata, - Wallet.Model.Message.AuthRequest.PayloadParams(type, chainId, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) - ) -} - -@JvmSynthetic -internal fun List.toSign(): List = mutableListOf().apply { - this@toSign.forEach { cacao: Wallet.Model.Cacao -> - with(cacao) { - add( - Sign.Model.Cacao( - Sign.Model.Cacao.Header(header.t), - Sign.Model.Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Sign.Model.Cacao.Signature(signature.t, signature.s, signature.m) - ) - ) - } - } -} - -@JvmSynthetic -internal fun Sign.Model.Cacao.toWallet(): Wallet.Model.Cacao = with(this) { - Wallet.Model.Cacao( - Wallet.Model.Cacao.Header(header.t), - Wallet.Model.Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Wallet.Model.Cacao.Signature(signature.t, signature.s, signature.m) - ) -} - -@JvmSynthetic -internal fun Sign.Model.ConnectionState.Reason.toWallet(): Wallet.Model.ConnectionState.Reason = when (this) { - is Sign.Model.ConnectionState.Reason.ConnectionClosed -> Wallet.Model.ConnectionState.Reason.ConnectionClosed(this.message) - is Sign.Model.ConnectionState.Reason.ConnectionFailed -> Wallet.Model.ConnectionState.Reason.ConnectionFailed(this.throwable) -} \ No newline at end of file diff --git a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Wallet.kt b/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Wallet.kt deleted file mode 100644 index 4de98c980..000000000 --- a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Wallet.kt +++ /dev/null @@ -1,345 +0,0 @@ -package com.walletconnect.web3.wallet.client - -import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface -import java.net.URI -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds - -object Wallet { - - sealed interface Listeners { - interface SessionPing : Listeners { - fun onSuccess(pingSuccess: Model.Ping.Success) - fun onError(pingError: Model.Ping.Error) - } - } - - sealed class Params { - data class Init(val core: CoreInterface) : Params() - - data class Pair(val uri: String) : Params() - - data class SessionApprove( - val proposerPublicKey: String, - val namespaces: Map, - val relayProtocol: String? = null, - ) : Params() - - data class ApproveSessionAuthenticate(val id: Long, val auths: List) : Params() - - data class RejectSessionAuthenticate(val id: Long, val reason: String) : Params() - - data class SessionReject(val proposerPublicKey: String, val reason: String) : Params() - - data class SessionUpdate(val sessionTopic: String, val namespaces: Map) : Params() - - data class SessionExtend(val topic: String) : Params() - - data class SessionEmit(val topic: String, val event: Model.SessionEvent, val chainId: String) : Params() - - data class SessionRequestResponse(val sessionTopic: String, val jsonRpcResponse: Model.JsonRpcResponse) : Params() - - data class SessionDisconnect(val sessionTopic: String) : Params() - - data class FormatMessage(val payloadParams: Model.PayloadParams, val issuer: String) : Params() - - data class FormatAuthMessage(val payloadParams: Model.PayloadAuthRequestParams, val issuer: String) : Params() - - data class Ping(val sessionTopic: String, val timeout: Duration = 30.seconds) : Params() - - sealed class AuthRequestResponse : Params() { - abstract val id: Long - - data class Result(override val id: Long, val signature: Model.Cacao.Signature, val issuer: String) : AuthRequestResponse() - data class Error(override val id: Long, val code: Int, val message: String) : AuthRequestResponse() - } - - data class DecryptMessage(val topic: String, val encryptedMessage: String) : Params() - } - - sealed class Model { - - sealed class Ping : Model() { - data class Success(val topic: String) : Ping() - data class Error(val error: Throwable) : Ping() - } - data class Error(val throwable: Throwable) : Model() - - data class ConnectionState(val isAvailable: Boolean, val reason: Reason? = null) : Model() { - sealed class Reason : Model() { - data class ConnectionClosed(val message: String) : Reason() - data class ConnectionFailed(val throwable: Throwable) : Reason() - } - } - - data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : Model() - data class ExpiredRequest(val topic: String, val id: Long) : Model() - - data class SessionProposal( - val pairingTopic: String, - val name: String, - val description: String, - val url: String, - val icons: List, - val redirect: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map, - val properties: Map?, - val proposerPublicKey: String, - val relayProtocol: String, - val relayData: String?, - ) : Model() - - data class SessionAuthenticate( - val id: Long, - val pairingTopic: String, - val participant: Participant, - val payloadParams: PayloadAuthRequestParams, - ) : Model() { - data class Participant( - val publicKey: String, - val metadata: Core.Model.AppMetaData?, - ) : Model() - } - - data class VerifyContext( - val id: Long, - val origin: String, - val validation: Validation, - val verifyUrl: String, - val isScam: Boolean?, - ) : Model() - - enum class Validation { - VALID, INVALID, UNKNOWN - } - - data class SessionRequest( - val topic: String, - val chainId: String?, - val peerMetaData: Core.Model.AppMetaData?, - val request: JSONRPCRequest, - ) : Model() { - - data class JSONRPCRequest( - val id: Long, - val method: String, - val params: String, - ) : Model() - } - - data class AuthRequest( - val id: Long, - val pairingTopic: String, - val payloadParams: PayloadParams, - ) : Model() - - sealed class SettledSessionResponse : Model() { - data class Result(val session: Session) : SettledSessionResponse() - data class Error(val errorMessage: String) : SettledSessionResponse() - } - - sealed class SessionUpdateResponse : Model() { - data class Result(val topic: String, val namespaces: Map) : SessionUpdateResponse() - data class Error(val errorMessage: String) : SessionUpdateResponse() - } - - sealed class SessionDelete : Model() { - data class Success(val topic: String, val reason: String) : SessionDelete() - data class Error(val error: Throwable) : SessionDelete() - } - - sealed class Namespace : Model() { - - //Required or Optional - data class Proposal( - val chains: List? = null, - val methods: List, - val events: List, - ) : Namespace() - - data class Session( - val chains: List? = null, - val accounts: List, - val methods: List, - val events: List, - ) : Namespace() - } - - sealed class JsonRpcResponse : Model() { - abstract val id: Long - val jsonrpc: String = "2.0" - - data class JsonRpcResult( - override val id: Long, - val result: String, - ) : JsonRpcResponse() - - data class JsonRpcError( - override val id: Long, - val code: Int, - val message: String, - ) : JsonRpcResponse() - } - - data class PayloadParams( - val type: String, - val chainId: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() - - data class PayloadAuthRequestParams( - val chains: List, - val domain: String, - val nonce: String, - val aud: String, - val type: String?, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List? - ) : Model() - - data class SessionEvent( - val name: String, - val data: String, - ) : Model() - - data class Event( - val topic: String, - val name: String, - val data: String, - val chainId: String, - ) : Model() - - data class Cacao( - val header: Header, - val payload: Payload, - val signature: Signature, - ) : Model() { - @Keep - data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface - data class Header(val t: String) : Model() - data class Payload( - val iss: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() { - val address: String get() = iss.split(ISS_DELIMITER)[ISS_POSITION_OF_ADDRESS] - - private companion object { - const val ISS_DELIMITER = ":" - const val ISS_POSITION_OF_ADDRESS = 4 - } - } - } - - data class Session( - @Deprecated("Pairing topic is deprecated") - val pairingTopic: String, - val topic: String, - val expiry: Long, - val requiredNamespaces: Map, - val optionalNamespaces: Map?, - val namespaces: Map, - val metaData: Core.Model.AppMetaData?, - ) : Model() { - val redirect: String? = metaData?.redirect - } - - data class PendingSessionRequest( - val requestId: Long, - val topic: String, - val method: String, - val chainId: String?, - val params: String, - ) : Model() - - data class PendingAuthRequest( - val id: Long, - val pairingTopic: String, - val payloadParams: PayloadParams, - ) : Model() - - sealed class Message : Model() { - - data class Simple( - val title: String, - val body: String, - ) : Message() - - data class SessionProposal( - val id: Long, - val pairingTopic: String, - val name: String, - val description: String, - val url: String, - val icons: List, - val redirect: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map, - val properties: Map?, - val proposerPublicKey: String, - val relayProtocol: String, - val relayData: String?, - ) : Message() - - data class SessionRequest( - val topic: String, - val chainId: String?, - val peerMetaData: Core.Model.AppMetaData?, - val request: JSONRPCRequest, - ) : Message() { - data class JSONRPCRequest( - val id: Long, - val method: String, - val params: String, - ) : Message() - } - - data class AuthRequest( - val id: Long, - val pairingTopic: String, - val metadata: Core.Model.AppMetaData, - val payloadParams: PayloadParams, - ) : Message() { - data class PayloadParams( - val type: String, - val chainId: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) - } - } - } -} \ No newline at end of file diff --git a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Web3Wallet.kt b/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Web3Wallet.kt deleted file mode 100644 index 52de508ca..000000000 --- a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/client/Web3Wallet.kt +++ /dev/null @@ -1,449 +0,0 @@ -package com.walletconnect.web3.wallet.client - -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.auth.client.Auth -import com.walletconnect.auth.client.AuthClient -import com.walletconnect.auth.common.exceptions.AuthClientAlreadyInitializedException -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.common.exceptions.SignClientAlreadyInitializedException -import kotlinx.coroutines.* -import java.util.* - -object Web3Wallet { - private lateinit var coreClient: CoreInterface - - interface WalletDelegate { - fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal, verifyContext: Wallet.Model.VerifyContext) - val onSessionAuthenticate: ((Wallet.Model.SessionAuthenticate, Wallet.Model.VerifyContext) -> Unit)? get() = null - fun onSessionRequest(sessionRequest: Wallet.Model.SessionRequest, verifyContext: Wallet.Model.VerifyContext) - fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) - fun onSessionExtend(session: Wallet.Model.Session) - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun onSessionAuthenticated(sessionAuthenticate: Wallet.Model.SessionAuthenticate, verifyContext: Wallet.Model.VerifyContext)") - ) - fun onAuthRequest(authRequest: Wallet.Model.AuthRequest, verifyContext: Wallet.Model.VerifyContext) - - //Responses - fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) - fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) - - //Utils - fun onProposalExpired(proposal: Wallet.Model.ExpiredProposal) { - //override me - } - - fun onRequestExpired(request: Wallet.Model.ExpiredRequest) { - //override me - } - - fun onConnectionStateChange(state: Wallet.Model.ConnectionState) - fun onError(error: Wallet.Model.Error) - } - - @Throws(IllegalStateException::class) - fun setWalletDelegate(delegate: WalletDelegate) { - val isSessionAuthenticateImplemented = delegate.onSessionAuthenticate != null - - val signWalletDelegate = object : SignClient.WalletDelegate { - override fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal, verifyContext: Sign.Model.VerifyContext) { - delegate.onSessionProposal(sessionProposal.toWallet(), verifyContext.toWallet()) - } - - override val onSessionAuthenticate: ((Sign.Model.SessionAuthenticate, Sign.Model.VerifyContext) -> Unit)? - get() = if (isSessionAuthenticateImplemented) { - { sessionAuthenticate, verifyContext -> - delegate.onSessionAuthenticate?.invoke(sessionAuthenticate.toWallet(), verifyContext.toWallet()) - } - } else { - null - } - - override fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest, verifyContext: Sign.Model.VerifyContext) { - delegate.onSessionRequest(sessionRequest.toWallet(), verifyContext.toWallet()) - } - - override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) { - delegate.onSessionDelete(deletedSession.toWallet()) - } - - override fun onSessionExtend(session: Sign.Model.Session) { - delegate.onSessionExtend(session.toWallet()) - } - - override fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) { - delegate.onSessionSettleResponse(settleSessionResponse.toWallet()) - } - - override fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) { - delegate.onSessionUpdateResponse(sessionUpdateResponse.toWallet()) - } - - override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { - delegate.onProposalExpired(proposal.toWallet()) - } - - override fun onRequestExpired(request: Sign.Model.ExpiredRequest) { - delegate.onRequestExpired(request.toWallet()) - } - - override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { - delegate.onConnectionStateChange(Wallet.Model.ConnectionState(state.isAvailable, state.reason?.toWallet())) - } - - override fun onError(error: Sign.Model.Error) { - delegate.onError(Wallet.Model.Error(error.throwable)) - } - } - - val authWalletDelegate = object : AuthClient.ResponderDelegate { - override fun onAuthRequest(authRequest: Auth.Event.AuthRequest, verifyContext: Auth.Event.VerifyContext) { - delegate.onAuthRequest(authRequest.toWallet(), verifyContext.toWallet()) - } - - override fun onConnectionStateChange(connectionStateChange: Auth.Event.ConnectionStateChange) { - //ignore - } - - override fun onError(error: Auth.Event.Error) { - delegate.onError(Wallet.Model.Error(error.error.throwable)) - } - } - - SignClient.setWalletDelegate(signWalletDelegate) - //TODO: Remove AuthClient setting responder delegate in the future - AuthClient.setResponderDelegate(authWalletDelegate) - } - - @Throws(IllegalStateException::class) - fun initialize(params: Wallet.Params.Init, onSuccess: () -> Unit = {}, onError: (Wallet.Model.Error) -> Unit) { - coreClient = params.core - var clientInitCounter = 0 - val onSuccessfulInitialization: () -> Unit = { clientInitCounter++ } - - SignClient.initialize(Sign.Params.Init(params.core), onSuccess = onSuccessfulInitialization) { error -> - if (error.throwable is SignClientAlreadyInitializedException) { - onSuccessfulInitialization() - } else { - onError(Wallet.Model.Error(error.throwable)) - } - } - //TODO: Remove AuthClient initialization in the future - AuthClient.initialize(Auth.Params.Init(params.core), onSuccess = onSuccessfulInitialization) { error -> - if (error.throwable is AuthClientAlreadyInitializedException) { - onSuccessfulInitialization() - } else { - onError(Wallet.Model.Error(error.throwable)) - } - } - validateInitializationCount(clientInitCounter, onSuccess, onError) - } - - @Throws(IllegalStateException::class) - fun registerDeviceToken(firebaseAccessToken: String, enableEncrypted: Boolean = false, onSuccess: () -> Unit, onError: (Wallet.Model.Error) -> Unit) { - coreClient.Echo.register(firebaseAccessToken, enableEncrypted, onSuccess) { error -> onError(Wallet.Model.Error(error)) } - } - - @Throws(IllegalStateException::class) - fun decryptMessage(params: Wallet.Params.DecryptMessage, onSuccess: (Wallet.Model.Message) -> Unit, onError: (Wallet.Model.Error) -> Unit) { - scope.launch { - SignClient.decryptMessage( - Sign.Params.DecryptMessage(params.topic, params.encryptedMessage), - onSuccess = { message -> - when (message) { - is Sign.Model.Message.SessionRequest -> onSuccess(message.toWallet()) - is Sign.Model.Message.SessionProposal -> onSuccess(message.toWallet()) - else -> { /*Ignore*/ - } - } - }, - onError = { signError -> onError(Wallet.Model.Error(signError.throwable)) }) - } - } - - @Throws(IllegalStateException::class) - fun dispatchEnvelope(urlWithEnvelope: String, onError: (Wallet.Model.Error) -> Unit) { - scope.launch { - try { - SignClient.dispatchEnvelope(urlWithEnvelope) { error -> onError(Wallet.Model.Error(error.throwable)) } - } catch (error: Exception) { - onError(Wallet.Model.Error(error)) - } - } - } - - @Throws(IllegalStateException::class) - fun pair(params: Wallet.Params.Pair, onSuccess: (Wallet.Params.Pair) -> Unit = {}, onError: (Wallet.Model.Error) -> Unit = {}) { - coreClient.Pairing.pair(Core.Params.Pair(params.uri), { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun approveSession( - params: Wallet.Params.SessionApprove, - onSuccess: (Wallet.Params.SessionApprove) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Approve(params.proposerPublicKey, params.namespaces.toSign(), params.relayProtocol) - SignClient.approveSession(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(Exception::class) - fun generateApprovedNamespaces(sessionProposal: Wallet.Model.SessionProposal, supportedNamespaces: Map): Map { - return com.walletconnect.sign.client.utils.generateApprovedNamespaces(sessionProposal.toSign(), supportedNamespaces.toSign()).toWallet() - } - - @Throws(IllegalStateException::class) - fun rejectSession( - params: Wallet.Params.SessionReject, - onSuccess: (Wallet.Params.SessionReject) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Reject(params.proposerPublicKey, params.reason) - SignClient.rejectSession(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun approveSessionAuthenticate( - params: Wallet.Params.ApproveSessionAuthenticate, - onSuccess: (Wallet.Params.ApproveSessionAuthenticate) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.ApproveAuthenticate(params.id, params.auths.toSign()) - SignClient.approveAuthenticate(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun rejectSessionAuthenticate( - params: Wallet.Params.RejectSessionAuthenticate, - onSuccess: (Wallet.Params.RejectSessionAuthenticate) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.RejectAuthenticate(params.id, params.reason) - SignClient.rejectAuthenticate(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(Exception::class) - fun generateAuthObject(payloadParams: Wallet.Model.PayloadAuthRequestParams, issuer: String, signature: Wallet.Model.Cacao.Signature): Wallet.Model.Cacao { - return com.walletconnect.sign.client.utils.generateAuthObject(payloadParams.toSign(), issuer, signature.toSign()).toWallet() - } - - @Throws(Exception::class) - fun generateAuthPayloadParams(payloadParams: Wallet.Model.PayloadAuthRequestParams, supportedChains: List, supportedMethods: List): Wallet.Model.PayloadAuthRequestParams { - return com.walletconnect.sign.client.utils.generateAuthPayloadParams(payloadParams.toSign(), supportedChains, supportedMethods).toWallet() - } - - @Throws(IllegalStateException::class) - fun updateSession( - params: Wallet.Params.SessionUpdate, - onSuccess: (Wallet.Params.SessionUpdate) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Update(params.sessionTopic, params.namespaces.toSign()) - SignClient.update(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun extendSession( - params: Wallet.Params.SessionExtend, - onSuccess: (Wallet.Params.SessionExtend) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Extend(params.topic) - SignClient.extend(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun respondSessionRequest( - params: Wallet.Params.SessionRequestResponse, - onSuccess: (Wallet.Params.SessionRequestResponse) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Response(params.sessionTopic, params.jsonRpcResponse.toSign()) - SignClient.respond(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - - @Throws(IllegalStateException::class) - fun emitSessionEvent( - params: Wallet.Params.SessionEmit, - onSuccess: (Wallet.Params.SessionEmit) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Emit(params.topic, params.event.toSign(), params.chainId) - SignClient.emit(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun disconnectSession( - params: Wallet.Params.SessionDisconnect, - onSuccess: (Wallet.Params.SessionDisconnect) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - val signParams = Sign.Params.Disconnect(params.sessionTopic) - SignClient.disconnect(signParams, { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - @Throws(IllegalStateException::class) - fun pingSession( - params: Wallet.Params.Ping, - sessionPing: Wallet.Listeners.SessionPing? - ) { - val signParams = Sign.Params.Ping(params.sessionTopic) - val signPingLister = object : Sign.Listeners.SessionPing { - override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { - sessionPing?.onSuccess(Wallet.Model.Ping.Success(pingSuccess.topic)) - } - - override fun onError(pingError: Sign.Model.Ping.Error) { - sessionPing?.onError(Wallet.Model.Ping.Error(pingError.error)) - } - } - - SignClient.ping(signParams, signPingLister) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - fun formatAuthMessage(params: Wallet.Params.FormatAuthMessage): String { - val signParams = Sign.Params.FormatMessage(params.payloadParams.toSign(), params.issuer) - return SignClient.formatAuthMessage(signParams) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun formatAuthMessage(formatMessage: Sign.Params.FormatMessage): String? in Web3Wallet SDK") - ) - @Throws(IllegalStateException::class) - fun formatMessage(params: Wallet.Params.FormatMessage): String? { - val authParams = Auth.Params.FormatMessage(params.payloadParams.toAuth(), params.issuer) - return AuthClient.formatMessage(authParams) - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun approveSessionAuthenticated(approve: Sign.Params.ApproveSessionAuthenticate, onSuccess: (Sign.Params.ApproveSessionAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) or fun rejectSessionAuthenticated(reject: Sign.Params.RejectSessionAuthenticate, onSuccess: (Sign.Params.RejectSessionAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) in Web3Wallet SDK") - ) - @Throws(IllegalStateException::class) - fun respondAuthRequest( - params: Wallet.Params.AuthRequestResponse, - onSuccess: (Wallet.Params.AuthRequestResponse) -> Unit = {}, - onError: (Wallet.Model.Error) -> Unit, - ) { - AuthClient.respond(params.toAuth(), { onSuccess(params) }, { error -> onError(Wallet.Model.Error(error.throwable)) }) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - @JvmStatic - fun getListOfActiveSessions(): List { - return SignClient.getListOfActiveSessions().map(Sign.Model.Session::toWallet) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - fun getActiveSessionByTopic(topic: String): Wallet.Model.Session? { - return SignClient.getActiveSessionByTopic(topic)?.toWallet() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - - @Deprecated( - "The return type of getPendingRequests methods has been replaced with SessionRequest list", - replaceWith = ReplaceWith("getPendingSessionRequests(topic: String): List") - ) - @Throws(IllegalStateException::class) - fun getPendingSessionRequests(topic: String): List { - return SignClient.getPendingRequests(topic).mapToPendingRequests() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - - @Throws(IllegalStateException::class) - fun getPendingListOfSessionRequests(topic: String): List { - return SignClient.getPendingSessionRequests(topic).mapToPendingSessionRequests() - } - - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - fun getSessionProposals(): List { - return SignClient.getSessionProposals().map(Sign.Model.SessionProposal::toWallet) - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun getPendingAuthenticateRequests(): List in Web3Wallet SDK") - ) - @Throws(IllegalStateException::class) - fun getPendingAuthRequests(): List { - return AuthClient.getPendingRequest().toWallet() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - fun getVerifyContext(id: Long): Wallet.Model.VerifyContext? { - return SignClient.getVerifyContext(id)?.toWallet() - } - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Throws(IllegalStateException::class) - fun getListOfVerifyContexts(): List { - return SignClient.getListOfVerifyContexts().map { verifyContext -> verifyContext.toWallet() } - } - - private fun validateInitializationCount(clientInitCounter: Int, onSuccess: () -> Unit, onError: (Wallet.Model.Error) -> Unit) { - scope.launch { - try { - withTimeout(TIMEOUT) { - while (true) { - if (clientInitCounter == 2) { - onSuccess() - return@withTimeout - } - delay(100) - } - } - } catch (e: Exception) { - onError(Wallet.Model.Error(e)) - } - } - } - - private const val TIMEOUT: Long = 10000 -} diff --git a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/utils/CacaoSigner.kt b/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/utils/CacaoSigner.kt deleted file mode 100644 index bbeae4e86..000000000 --- a/product/web3wallet/src/main/kotlin/com/walletconnect/web3/wallet/utils/CacaoSigner.kt +++ /dev/null @@ -1,16 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.web3.wallet.utils - -import com.walletconnect.android.cacao.signature.ISignatureType -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.web3.wallet.client.Wallet - - -/// Only added to have backwards compatibility. Newer SDKs should only add CacaoSigner object below. -@Deprecated("Moved to android-core module, as other SDKs also need CACAO.", ReplaceWith("com.walletconnect.android.internal.common.cacao.signature.SignatureType")) -enum class SignatureType(override val header: String) : ISignatureType { - EIP191("eip191"), EIP1271("eip1271"); -} - -object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/product/web3wallet/web3wallet-rules.pro b/product/web3wallet/web3wallet-rules.pro deleted file mode 100644 index 733da187b..000000000 --- a/product/web3wallet/web3wallet-rules.pro +++ /dev/null @@ -1,4 +0,0 @@ --keep class com.walletconnect.web3.wallet.client.Wallet$Model$Cacao$Signature { *; } --keep class com.walletconnect.web3.wallet.client.Wallet$Model$Cacao { *; } --keep class com.walletconnect.web3.wallet.client.Wallet$Model { *; } --keep class com.walletconnect.web3.wallet.client.Wallet { *; } \ No newline at end of file diff --git a/protocol/auth/ReadMe.md b/protocol/auth/ReadMe.md deleted file mode 100644 index 19dfeea81..000000000 --- a/protocol/auth/ReadMe.md +++ /dev/null @@ -1,47 +0,0 @@ -# **WalletConnect Auth - Kotlin** - -Kotlin implementation of WalletConnect Auth protocol for Android applications. - -![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/auth) - -## Requirements - -* Android min SDK 23 -* Java 11 - -## Documentation and usage - -* [Installation guide](https://docs.walletconnect.com/2.0/kotlin/auth/installation) -* [Responder](https://docs.walletconnect.com/2.0/kotlin/auth/wallet-or-responder-usage) -* [Requester](https://docs.walletconnect.com/2.0/kotlin/auth/dapp-or-requester-usage) -* [Protocol specification](https://docs.walletconnect.com/2.0/specs/auth/) - -  - -## Installation - -root/build.gradle.kts: - -```gradle -allprojects { - repositories { - mavenCentral() - maven { url "https://jitpack.io" } - } -} -``` - -app/build.gradle.kts - -```gradle -implementation(platform("com.walletconnect:android-bom:{BOM version}")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:auth") -``` - -  - -## Sample apps - -* For sample responder/wallet run `responder module` -* For sample requester/Dapp run `requester module` diff --git a/protocol/auth/build.gradle.kts b/protocol/auth/build.gradle.kts deleted file mode 100644 index 930851a96..000000000 --- a/protocol/auth/build.gradle.kts +++ /dev/null @@ -1,96 +0,0 @@ -plugins { - id("com.android.library") - id(libs.plugins.kotlin.android.get().pluginId) - alias(libs.plugins.sqlDelight) - alias(libs.plugins.google.ksp) - id("publish-module-android") - id("jacoco-report") -} - -project.apply { - extra[KEY_PUBLISH_ARTIFACT_ID] = AUTH - extra[KEY_PUBLISH_VERSION] = AUTH_VERSION - extra[KEY_SDK_NAME] = "Auth" -} - -android { - namespace = "com.walletconnect.auth" - compileSdk = COMPILE_SDK - - defaultConfig { - minSdk = MIN_SDK - - buildConfigField( - type = "String", - name = "SDK_VERSION", - value = "\"${requireNotNull(extra.get(KEY_PUBLISH_VERSION))}\"" - ) - buildConfigField( - "String", - "PROJECT_ID", - "\"${System.getenv("WC_CLOUD_PROJECT_ID") ?: ""}\"" - ) - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = jvmVersion - targetCompatibility = jvmVersion - } - - lint { - abortOnError = true - ignoreWarnings = true - warningsAsErrors = false - } - - - kotlinOptions { - jvmTarget = jvmVersion.toString() - } - - buildFeatures { - buildConfig = true - } -} - -sqldelight { - databases { - create("AuthDatabase") { - packageName.set("com.walletconnect.auth") - schemaOutputDirectory.set(file("src/main/sqldelight/databases")) - generateAsync.set(true) - verifyMigrations.set(true) - verifyDefinitions.set(true) - } - } -} - - -dependencies { - debugImplementation(project(":core:android")) - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") - - ksp(libs.moshi.ksp) - implementation(libs.bundles.sqlDelight) - api(libs.web3jCrypto) - - testImplementation(libs.bundles.androidxTest) - testImplementation(libs.robolectric) - testImplementation(libs.json) - testImplementation(libs.coroutines.test) - testImplementation(libs.bundles.scarlet.test) - testImplementation(libs.bundles.sqlDelight.test) - - androidTestUtil(libs.androidx.testOrchestrator) - androidTestImplementation(libs.bundles.androidxAndroidTest) -} \ No newline at end of file diff --git a/protocol/auth/proguard-rules.pro b/protocol/auth/proguard-rules.pro deleted file mode 100644 index 88c41390a..000000000 --- a/protocol/auth/proguard-rules.pro +++ /dev/null @@ -1 +0,0 @@ --keepnames public class com.walletconnect.auth.** { *; } \ No newline at end of file diff --git a/protocol/auth/src/main/AndroidManifest.xml b/protocol/auth/src/main/AndroidManifest.xml deleted file mode 100644 index 93d8dbc09..000000000 --- a/protocol/auth/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/Auth.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/Auth.kt deleted file mode 100644 index 7ba6301b5..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/Auth.kt +++ /dev/null @@ -1,164 +0,0 @@ -package com.walletconnect.auth.client - -import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.internal.common.signing.cacao.Issuer - -object Auth { - - sealed class Event { - data class AuthRequest( - val id: Long, - val pairingTopic: String, - val payloadParams: Model.PayloadParams, - ) : Event() - - data class AuthResponse(val response: Model.Response) : Event() - - data class VerifyContext( - val id: Long, - val origin: String, - val validation: Model.Validation, - val verifyUrl: String, - val isScam: Boolean? - ) : Event() - - data class ConnectionStateChange( - val state: Model.ConnectionState, - ) : Event() - - data class Error( - val error: Model.Error, - ) : Event() - } - - sealed class Model { - - data class Error(val throwable: Throwable) : Model() - - data class ConnectionState(val isAvailable: Boolean) : Model() - - enum class Validation { - VALID, INVALID, UNKNOWN - } - - data class PendingRequest( - val id: Long, - val pairingTopic: String, - val payloadParams: PayloadParams, - ) : Model() - - data class VerifyContext( - val id: Long, - val origin: String, - val validation: Validation, - val verifyUrl: String, - val isScam: Boolean? - ) : Event() - - data class PayloadParams( - val type: String, - val chainId: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() - - data class Cacao( - val header: Header, - val payload: Payload, - val signature: Signature, - ) : Model() { - @Keep - data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface - data class Header(val t: String) : Model() - data class Payload( - val iss: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() { - val address: String - get() = Issuer(iss).address - } - } - - sealed class Response : Model() { - abstract val id: Long - - data class Result(override val id: Long, val cacao: Cacao) : Response() - data class Error(override val id: Long, val code: Int, val message: String) : Response() - } - - sealed class Message { - data class AuthRequest( - val id: Long, - val pairingTopic: String, - val metadata: Core.Model.AppMetaData, - val payloadParams: PayloadParams, - ) : Message() { - data class PayloadParams( - val type: String, - val chainId: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) - } - } - } - - sealed class Params { - - data class Init(val core: CoreInterface) : Params() - - data class Request( - val topic: String, - val chainId: String, - val domain: String, - val nonce: String, - val aud: String, - val type: String?, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - val expiry: Long? = null, - ) : Params() - - sealed class Respond : Params() { - abstract val id: Long - - data class Result(override val id: Long, val signature: Model.Cacao.Signature, val issuer: String) : Respond() - data class Error(override val id: Long, val code: Int, val message: String) : Respond() - } - - data class FormatMessage(val payloadParams: Model.PayloadParams, val issuer: String) : Params() - - data class DecryptMessage(val topic: String, val encryptedMessage: String) : Params() - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthClient.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthClient.kt deleted file mode 100644 index 8aa77574d..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthClient.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.auth.client - -object AuthClient : AuthInterface by AuthProtocol.instance { - interface RequesterDelegate : AuthInterface.RequesterDelegate - interface ResponderDelegate : AuthInterface.ResponderDelegate -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthInterface.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthInterface.kt deleted file mode 100644 index 8e3c35505..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthInterface.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.walletconnect.auth.client - -interface AuthInterface { - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - sealed interface AuthDelegate { - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - fun onConnectionStateChange(connectionStateChange: Auth.Event.ConnectionStateChange) - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - fun onError(error: Auth.Event.Error) - } - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - interface RequesterDelegate : AuthDelegate { - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse)") - ) - fun onAuthResponse(authResponse: Auth.Event.AuthResponse) - } - - interface ResponderDelegate : AuthDelegate { - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun onSessionAuthenticated(sessionAuthenticate: Wallet.Model.SessionAuthenticate, verifyContext: Wallet.Model.VerifyContext)") - ) - fun onAuthRequest(authRequest: Auth.Event.AuthRequest, verifyContext: Auth.Event.VerifyContext) - } - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - fun setRequesterDelegate(delegate: RequesterDelegate) - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - fun setResponderDelegate(delegate: ResponderDelegate) - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - fun initialize(params: Auth.Params.Init, onSuccess: () -> Unit = {}, onError: (Auth.Model.Error) -> Unit) - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun sessionAuthenticate(authenticate: Sign.Params.Authenticate, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - fun request(params: Auth.Params.Request, onSuccess: () -> Unit, onError: (Auth.Model.Error) -> Unit) - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun approveSessionAuthenticated(approve: Sign.Params.ApproveSessionAuthenticate, onSuccess: (Sign.Params.ApproveSessionAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit)"), - ) - fun respond(params: Auth.Params.Respond, onSuccess: (Auth.Params.Respond) -> Unit = {}, onError: (Auth.Model.Error) -> Unit) - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun decryptMessage(params: Sign.Params.DecryptMessage, onSuccess: (Sign.Model.Message) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - fun decryptMessage(params: Auth.Params.DecryptMessage, onSuccess: (Auth.Model.Message.AuthRequest) -> Unit, onError: (Auth.Model.Error) -> Unit) - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun formatAuthMessage(formatMessage: Sign.Params.FormatMessage): String?") - ) - fun formatMessage(params: Auth.Params.FormatMessage): String? - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun getPendingAuthenticateRequests(): List") - ) - fun getPendingRequest(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("override fun getVerifyContext(id: Long): Sign.Model.VerifyContext?") - ) - fun getVerifyContext(id: Long): Auth.Model.VerifyContext? - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("override fun getListOfVerifyContexts(): List") - ) - fun getListOfVerifyContexts(): List -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthProtocol.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthProtocol.kt deleted file mode 100644 index bcc9bd803..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/AuthProtocol.kt +++ /dev/null @@ -1,191 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.client - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.auth.client.mapper.toAuth -import com.walletconnect.auth.client.mapper.toClient -import com.walletconnect.auth.client.mapper.toClientAuthContext -import com.walletconnect.auth.client.mapper.toClientAuthRequest -import com.walletconnect.auth.client.mapper.toCommon -import com.walletconnect.auth.common.model.Events -import com.walletconnect.auth.di.engineModule -import com.walletconnect.auth.di.jsonRpcModule -import com.walletconnect.auth.engine.domain.AuthEngine -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.koin.core.KoinApplication - -internal class AuthProtocol(private val koinApp: KoinApplication = wcKoinApp) : AuthInterface { - private lateinit var authEngine: AuthEngine - - companion object { - val instance = AuthProtocol() - } - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - @Throws(IllegalStateException::class) - override fun initialize(params: Auth.Params.Init, onSuccess: () -> Unit, onError: (Auth.Model.Error) -> Unit) { - if (!::authEngine.isInitialized) { - try { - koinApp.modules( - jsonRpcModule(), - engineModule(), - ) - - authEngine = koinApp.koin.get() - authEngine.setup() - onSuccess() - } catch (e: Exception) { - onError(Auth.Model.Error(e)) - } - } else { - onError(Auth.Model.Error(IllegalStateException("AuthClient already initialized"))) - } - } - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - @Throws(IllegalStateException::class) - override fun setRequesterDelegate(delegate: AuthInterface.RequesterDelegate) { - checkEngineInitialization() - authEngine.engineEvent.onEach { event -> - when (event) { - is ConnectionState -> delegate.onConnectionStateChange(event.toClient()) - is SDKError -> delegate.onError(event.toClient()) - is Events.OnAuthResponse -> delegate.onAuthResponse(event.toClient()) - } - }.launchIn(scope) - } - - @Deprecated("AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.") - @Throws(IllegalStateException::class) - override fun setResponderDelegate(delegate: AuthInterface.ResponderDelegate) { - checkEngineInitialization() - authEngine.engineEvent.onEach { event -> - when (event) { - is ConnectionState -> delegate.onConnectionStateChange(event.toClient()) - is SDKError -> delegate.onError(event.toClient()) - is Events.OnAuthRequest -> delegate.onAuthRequest(event.toClientAuthRequest(), event.toClientAuthContext()) - } - }.launchIn(scope) - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun sessionAuthenticate(authenticate: Sign.Params.Authenticate, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - @Throws(IllegalStateException::class) - override fun request(params: Auth.Params.Request, onSuccess: () -> Unit, onError: (Auth.Model.Error) -> Unit) { - checkEngineInitialization() - - scope.launch { - try { - val expiry = params.expiry?.run { Expiry(this) } - authEngine.request(params.toCommon(), expiry, params.topic, - onSuccess = onSuccess, - onFailure = { error -> onError(Auth.Model.Error(error)) } - ) - } catch (error: Exception) { - onError(Auth.Model.Error(error)) - } - } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun approveSessionAuthenticated(approve: Sign.Params.ApproveSessionAuthenticate, onSuccess: (Sign.Params.ApproveSessionAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - @Throws(IllegalStateException::class) - override fun respond(params: Auth.Params.Respond, onSuccess: (Auth.Params.Respond) -> Unit, onError: (Auth.Model.Error) -> Unit) { - checkEngineInitialization() - - scope.launch { - try { - authEngine.respond(params.toCommon(), { onSuccess(params) }, { error -> onError(Auth.Model.Error(error)) }) - } catch (error: Exception) { - onError(Auth.Model.Error(error)) - } - } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun formatAuthMessage(formatMessage: Sign.Params.FormatMessage): String?") - ) - @Throws(IllegalStateException::class) - override fun formatMessage(params: Auth.Params.FormatMessage): String? { - checkEngineInitialization() - - return try { - runBlocking { authEngine.formatMessage(params.payloadParams.toCommon(), params.issuer) } - } catch (error: Exception) { - null - } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun decryptMessage(params: Sign.Params.DecryptMessage, onSuccess: (Sign.Model.Message) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - override fun decryptMessage(params: Auth.Params.DecryptMessage, onSuccess: (Auth.Model.Message.AuthRequest) -> Unit, onError: (Auth.Model.Error) -> Unit) { - checkEngineInitialization() - - scope.launch { - try { - authEngine.decryptNotification( - topic = params.topic, - message = params.encryptedMessage, - onSuccess = { message -> (message as? Core.Model.Message.AuthRequest)?.run { onSuccess(message.toAuth()) } }, - onFailure = { error -> onError(Auth.Model.Error(error)) } - ) - } catch (error: Exception) { - onError(Auth.Model.Error(error)) - } - } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("fun getPendingAuthenticateRequests(): List") - ) - @Throws(IllegalStateException::class) - override fun getPendingRequest(): List { - checkEngineInitialization() - - return runBlocking { authEngine.getPendingRequests().toClient() } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("override fun getVerifyContext(id: Long): Sign.Model.VerifyContext?") - ) - @Throws(IllegalStateException::class) - override fun getVerifyContext(id: Long): Auth.Model.VerifyContext? { - checkEngineInitialization() - return runBlocking { authEngine.getVerifyContext(id)?.toClient() } - } - - @Deprecated( - "AuthSDK has been deprecated. Please use updated Web3Wallet and Sign SDKs instead.", - replaceWith = ReplaceWith("override fun getListOfVerifyContexts(): List") - ) - @Throws(IllegalStateException::class) - override fun getListOfVerifyContexts(): List { - checkEngineInitialization() - return runBlocking { authEngine.getListOfVerifyContext().map { verifyContext -> verifyContext.toClient() } } - } - - @Throws(IllegalStateException::class) - private fun checkEngineInitialization() { - check(::authEngine.isInitialized) { - "AuthClient needs to be initialized first using the initialize function" - } - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/mapper/ClientMapper.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/mapper/ClientMapper.kt deleted file mode 100644 index 35eecced6..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/client/mapper/ClientMapper.kt +++ /dev/null @@ -1,136 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.client.mapper - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.auth.client.Auth -import com.walletconnect.auth.common.model.AuthResponse -import com.walletconnect.auth.common.model.Events -import com.walletconnect.auth.common.model.PayloadParams -import com.walletconnect.auth.common.model.PendingRequest -import com.walletconnect.auth.common.model.Respond -import java.text.SimpleDateFormat -import java.util.* - -@JvmSynthetic -internal fun Auth.Params.Respond.toCommon(): Respond = when (this) { - is Auth.Params.Respond.Result -> Respond.Result(id, signature, issuer) - is Auth.Params.Respond.Error -> Respond.Error(id, code, message) -} - -@JvmSynthetic -internal fun ConnectionState.toClient(): Auth.Event.ConnectionStateChange = - Auth.Event.ConnectionStateChange(Auth.Model.ConnectionState(this.isAvailable)) - -@JvmSynthetic -internal fun SDKError.toClient(): Auth.Event.Error = Auth.Event.Error(Auth.Model.Error(this.exception)) - -@JvmSynthetic -internal fun Events.OnAuthRequest.toClientAuthRequest(): Auth.Event.AuthRequest = Auth.Event.AuthRequest(id, pairingTopic, payloadParams.toClient()) - -@JvmSynthetic -internal fun Events.OnAuthRequest.toClientAuthContext(): Auth.Event.VerifyContext = - Auth.Event.VerifyContext(id, verifyContext.origin, verifyContext.validation.toClientValidation(), verifyContext.verifyUrl, verifyContext.isScam) - -@JvmSynthetic -internal fun Validation.toClientValidation(): Auth.Model.Validation = - when (this) { - Validation.VALID -> Auth.Model.Validation.VALID - Validation.INVALID -> Auth.Model.Validation.INVALID - Validation.UNKNOWN -> Auth.Model.Validation.UNKNOWN - } - -internal fun PayloadParams.toClient(): Auth.Model.PayloadParams = - Auth.Model.PayloadParams( - type = type, - chainId = chainId, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - iat = iat, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - ) - -@JvmSynthetic -internal fun Events.OnAuthResponse.toClient(): Auth.Event.AuthResponse = when (val response = response) { - is AuthResponse.Error -> Auth.Event.AuthResponse(Auth.Model.Response.Error(id, response.code, response.message)) - is AuthResponse.Result -> Auth.Event.AuthResponse(Auth.Model.Response.Result(id, response.cacao.toClient())) -} - -@JvmSynthetic -internal fun Auth.Params.Request.toCommon(): PayloadParams = PayloadParams( - type = CacaoType.EIP4361.header, - chainId = chainId, - domain = domain, - aud = aud, - version = Cacao.Payload.CURRENT_VERSION, - nonce = nonce, - iat = SimpleDateFormat(Cacao.Payload.ISO_8601_PATTERN).format(Calendar.getInstance().time), - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, -) - -@JvmSynthetic -internal fun List.toClient(): List = - map { request -> - Auth.Model.PendingRequest( - request.id, - request.pairingTopic, - request.payloadParams.toClient() - ) - } - -@JvmSynthetic -internal fun Auth.Model.PayloadParams.toCommon(): PayloadParams = - PayloadParams( - type = type, - chainId = chainId, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - iat = iat, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - ) - -@JvmSynthetic -internal fun Auth.Model.Cacao.Signature.toCommon(): Cacao.Signature = Cacao.Signature(t, s, m) - -@JvmSynthetic -internal fun Cacao.toClient(): Auth.Model.Cacao = Auth.Model.Cacao(header.toClient(), payload.toClient(), signature.toClient()) - -@JvmSynthetic -internal fun Cacao.Header.toClient(): Auth.Model.Cacao.Header = Auth.Model.Cacao.Header(t) - -@JvmSynthetic -internal fun Cacao.Payload.toClient(): Auth.Model.Cacao.Payload = - Auth.Model.Cacao.Payload(iss, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) - -@JvmSynthetic -internal fun Cacao.Signature.toClient(): Auth.Model.Cacao.Signature = Auth.Model.Cacao.Signature(t, s, m) - -@JvmSynthetic -internal fun VerifyContext.toClient(): Auth.Model.VerifyContext = Auth.Model.VerifyContext(id, origin, validation.toClientValidation(), verifyUrl, isScam) - -@JvmSynthetic -internal fun Core.Model.Message.AuthRequest.toAuth(): Auth.Model.Message.AuthRequest = with(payloadParams) { - Auth.Model.Message.AuthRequest(id, pairingTopic, metadata, Auth.Model.Message.AuthRequest.PayloadParams(type, chainId, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources)) -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Exceptions.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Exceptions.kt deleted file mode 100644 index e78f305e7..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Exceptions.kt +++ /dev/null @@ -1,13 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.exceptions - -import com.walletconnect.android.internal.common.exception.WalletConnectException - -internal object MissingAuthRequestException : WalletConnectException(MISSING_AUTH_REQUEST_MESSAGE) -internal object InvalidCacaoException : WalletConnectException(CACAO_IS_NOT_VALID_MESSAGE) -internal class InvalidParamsException(override val message: String) : WalletConnectException(message) - -class AuthClientAlreadyInitializedException : WalletConnectException(CLIENT_ALREADY_INITIALIZED) - -class InvalidAuthParamsType : WalletConnectException(INVALID_AUTH_PARAMS_TYPE) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Messages.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Messages.kt deleted file mode 100644 index 3fd34516d..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/Messages.kt +++ /dev/null @@ -1,9 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.exceptions - -internal const val MISSING_AUTH_REQUEST_MESSAGE: String = "Missing Auth Request" -internal const val CACAO_IS_NOT_VALID_MESSAGE: String = "Cacao is not valid" - -internal const val CLIENT_ALREADY_INITIALIZED: String = "AuthClient already initialized" -internal const val INVALID_AUTH_PARAMS_TYPE: String = "Invalid Auth params type" diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/PeerError.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/PeerError.kt deleted file mode 100644 index 99fb2c132..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/exceptions/PeerError.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.exceptions - -import com.walletconnect.android.internal.common.model.type.Error - -internal sealed class PeerError : Error { - object SignatureVerificationFailed : PeerError() { - override val message: String = "Signature verification failed" - override val code: Int = 11004 - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthParams.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthParams.kt deleted file mode 100644 index d3cc4ccfa..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthParams.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.walletconnect.auth.common.json_rpc - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.auth.common.model.PayloadParams -import com.walletconnect.auth.common.model.Requester - -internal sealed class AuthParams : ClientParams { - - @JsonClass(generateAdapter = true) - internal data class RequestParams( - @Json(name = "requester") - val requester: Requester, - @Json(name = "payloadParams") - val payloadParams: PayloadParams, - @Json(name = "expiry") - val expiry: Expiry? = null - ) : AuthParams() -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthRpc.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthRpc.kt deleted file mode 100644 index 0f695c595..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/json_rpc/AuthRpc.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.json_rpc - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.util.generateId - -internal sealed class AuthRpc : JsonRpcClientSync { - - @JsonClass(generateAdapter = true) - internal data class AuthRequest( - @Json(name = "id") - override val id: Long = generateId(), - @Json(name = "jsonrpc") - override val jsonrpc: String = "2.0", - @Json(name = "method") - override val method: String = JsonRpcMethod.WC_AUTH_REQUEST, - @Json(name = "params") - override val params: AuthParams.RequestParams - ) : AuthRpc() -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/AuthResponse.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/AuthResponse.kt deleted file mode 100644 index d90b3ee0a..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/AuthResponse.kt +++ /dev/null @@ -1,10 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.model - -import com.walletconnect.android.internal.common.signing.cacao.Cacao - -internal sealed class AuthResponse { - data class Result(val cacao: Cacao) : AuthResponse() - data class Error(val code: Int, val message: String) : AuthResponse() -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Events.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Events.kt deleted file mode 100644 index 5fa467128..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Events.kt +++ /dev/null @@ -1,11 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.verify.model.VerifyContext - -internal sealed class Events : EngineEvent { - data class OnAuthRequest(val id: Long, val pairingTopic: String, val payloadParams: PayloadParams, val verifyContext: VerifyContext) : Events() - data class OnAuthResponse(val id: Long, val response: AuthResponse) : Events() -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/JsonRpcHistoryEntry.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/JsonRpcHistoryEntry.kt deleted file mode 100644 index 96e2337ec..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/JsonRpcHistoryEntry.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.auth.common.model - -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.foundation.common.model.Topic - -internal data class JsonRpcHistoryEntry( - val id: Long, - val topic: Topic, - val method: String, - val params: AuthParams.RequestParams, - val response: String? -) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PayloadParams.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PayloadParams.kt deleted file mode 100644 index 30414a828..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PayloadParams.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -internal data class PayloadParams( - @Json(name = "type") - val type: String, - @Json(name = "chainId") - val chainId: String, - @Json(name = "domain") - val domain: String, - @Json(name = "aud") - val aud: String, - @Json(name = "version") - val version: String, - @Json(name = "nonce") - val nonce: String, - @Json(name = "iat") - val iat: String, - @Json(name = "nbf") - val nbf: String?, - @Json(name = "exp") - val exp: String?, - @Json(name = "statement") - val statement: String?, - @Json(name = "requestId") - val requestId: String?, - @Json(name = "resources") - val resources: List?, -) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PendingRequest.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PendingRequest.kt deleted file mode 100644 index 943a3f963..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/PendingRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.auth.common.model - -internal data class PendingRequest( - val id: Long, - val pairingTopic: String, - val payloadParams: PayloadParams -) diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Requester.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Requester.kt deleted file mode 100644 index b4a28e791..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Requester.kt +++ /dev/null @@ -1,15 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.AppMetaData - -@JsonClass(generateAdapter = true) -internal data class Requester( - @Json(name = "publicKey") - val publicKey: String, - @Json(name = "metadata") - val metadata: AppMetaData -) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Respond.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Respond.kt deleted file mode 100644 index dc780370c..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Respond.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.common.model - -import com.walletconnect.auth.client.Auth - -internal sealed class Respond { - abstract val id: Long - - data class Result(override val id: Long, val signature: Auth.Model.Cacao.Signature, val iss: String) : Respond() - data class Error(override val id: Long, val code: Int, val message: String) : Respond() -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Response.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Response.kt deleted file mode 100644 index e2bc32da5..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/common/model/Response.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.auth.common.model - -import com.walletconnect.android.internal.common.signing.cacao.Cacao - -internal sealed class Response { - abstract val id: Long - - data class Result(override val id: Long, val cacao: Cacao) : Response() - data class Error(override val id: Long, val code: Int, val message: String) : Response() -} diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/CallsModule.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/CallsModule.kt deleted file mode 100644 index e5d2d0bdd..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/CallsModule.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.walletconnect.auth.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.auth.use_case.calls.DecryptAuthMessageUseCase -import com.walletconnect.auth.use_case.calls.FormatMessageUseCase -import com.walletconnect.auth.use_case.calls.FormatMessageUseCaseInterface -import com.walletconnect.auth.use_case.calls.GetListOfVerifyContextsUseCase -import com.walletconnect.auth.use_case.calls.GetListOfVerifyContextsUseCaseInterface -import com.walletconnect.auth.use_case.calls.GetVerifyContextUseCase -import com.walletconnect.auth.use_case.calls.GetVerifyContextUseCaseInterface -import com.walletconnect.auth.use_case.calls.RespondAuthRequestUseCase -import com.walletconnect.auth.use_case.calls.RespondAuthRequestUseCaseInterface -import com.walletconnect.auth.use_case.calls.SendAuthRequestUseCase -import com.walletconnect.auth.use_case.calls.SendAuthRequestUseCaseInterface -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun callsModule() = module { - - single { SendAuthRequestUseCase(crypto = get(), jsonRpcInteractor = get(), selfAppMetaData = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { - RespondAuthRequestUseCase( - crypto = get(), - jsonRpcInteractor = get(), - verifyContextStorageRepository = get(), - getPendingJsonRpcHistoryEntryByIdUseCase = get(), - cacaoVerifier = get(), - pairingController = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single(named(AndroidCommonDITags.DECRYPT_AUTH_MESSAGE)) { - val useCase = DecryptAuthMessageUseCase( - codec = get(), - serializer = get(), - metadataRepository = get(), - pushMessageStorageRepository = get() - ) - - get>(named(AndroidCommonDITags.DECRYPT_USE_CASES))[Tags.AUTH_REQUEST.id.toString()] = useCase - useCase - } - - single { FormatMessageUseCase() } - - single { GetVerifyContextUseCase(verifyContextStorageRepository = get()) } - - single { GetListOfVerifyContextsUseCase(verifyContextStorageRepository = get()) } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/EngineModule.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/EngineModule.kt deleted file mode 100644 index 5e20a36e6..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/EngineModule.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.walletconnect.auth.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.auth.engine.domain.AuthEngine -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntriesUseCase -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntriesUseCaseInterface -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntryByTopicUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun engineModule() = module { - - includes(callsModule(), requestsModule(), responsesModule()) - - single { GetPendingJsonRpcHistoryEntriesUseCase(jsonRpcHistory = get(), serializer = get()) } - single { GetPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcHistory = get(), serializer = get()) } - single { GetPendingJsonRpcHistoryEntryByTopicUseCase(jsonRpcHistory = get(), serializer = get()) } - single { CacaoVerifier(projectId = get()) } - single { - AuthEngine( - jsonRpcInteractor = get(), - verifyContextStorageRepository = get(), - getListOfVerifyContextsUseCase = get(), - getVerifyContextUseCase = get(), - formatMessageUseCase = get(), - onAuthRequestUseCase = get(), - onAuthRequestResponseUseCase = get(), - respondAuthRequestUseCase = get(), - sendAuthRequestUseCase = get(), - pairingHandler = get(), - getPendingJsonRpcHistoryEntriesUseCase = get(), - getPendingJsonRpcHistoryEntryByTopicUseCase = get(), - decryptAuthMessageUseCase = get(named(AndroidCommonDITags.DECRYPT_AUTH_MESSAGE)), - ) - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/JsonRpcModule.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/JsonRpcModule.kt deleted file mode 100644 index e0e3c8d2e..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/JsonRpcModule.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.auth.di - -import com.walletconnect.auth.common.json_rpc.AuthRpc -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.utils.addDeserializerEntry -import com.walletconnect.utils.addSerializerEntry -import org.koin.dsl.module - -@JvmSynthetic -internal fun jsonRpcModule() = module { - - addSerializerEntry(AuthRpc.AuthRequest::class) - - addDeserializerEntry(JsonRpcMethod.WC_AUTH_REQUEST, AuthRpc.AuthRequest::class) -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/RequestsModule.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/RequestsModule.kt deleted file mode 100644 index 416553a13..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/RequestsModule.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.auth.di - -import com.walletconnect.auth.use_case.requests.OnAuthRequestUseCase -import org.koin.dsl.module - -@JvmSynthetic -internal fun requestsModule() = module { - single { OnAuthRequestUseCase(jsonRpcInteractor = get(), resolveAttestationIdUseCase = get(), pairingController = get()) } -} diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/ResponsesModule.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/ResponsesModule.kt deleted file mode 100644 index 5caeb9ea5..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/di/ResponsesModule.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.auth.di - -import com.walletconnect.auth.use_case.responses.OnAuthRequestResponseUseCase -import org.koin.dsl.module - -@JvmSynthetic -internal fun responsesModule() = module { - single { OnAuthRequestResponseUseCase(cacaoVerifier = get(), pairingHandler = get(), pairingInterface = get()) } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/PairingTopicToReponseMap.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/PairingTopicToReponseMap.kt deleted file mode 100644 index 622ff5afb..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/PairingTopicToReponseMap.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.auth.engine - -import com.walletconnect.foundation.common.model.Topic - -// idea: If we need responseTopic persistence throughout app terminations this is not sufficient -internal val pairingTopicToResponseTopicMap: MutableMap = mutableMapOf() \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/domain/AuthEngine.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/domain/AuthEngine.kt deleted file mode 100644 index 76bf06d64..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/domain/AuthEngine.kt +++ /dev/null @@ -1,143 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.engine.domain - -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.model.Events -import com.walletconnect.auth.engine.pairingTopicToResponseTopicMap -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntriesUseCaseInterface -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntryByTopicUseCase -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.auth.use_case.calls.FormatMessageUseCaseInterface -import com.walletconnect.auth.use_case.calls.GetListOfVerifyContextsUseCaseInterface -import com.walletconnect.auth.use_case.calls.GetVerifyContextUseCaseInterface -import com.walletconnect.auth.use_case.calls.RespondAuthRequestUseCaseInterface -import com.walletconnect.auth.use_case.calls.SendAuthRequestUseCaseInterface -import com.walletconnect.auth.use_case.requests.OnAuthRequestUseCase -import com.walletconnect.auth.use_case.responses.OnAuthRequestResponseUseCase -import com.walletconnect.utils.Empty -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class AuthEngine( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val verifyContextStorageRepository: VerifyContextStorageRepository, - private val getPendingJsonRpcHistoryEntriesUseCase: GetPendingJsonRpcHistoryEntriesUseCaseInterface, - private val getPendingJsonRpcHistoryEntryByTopicUseCase: GetPendingJsonRpcHistoryEntryByTopicUseCase, - private val pairingHandler: PairingControllerInterface, - private val sendAuthRequestUseCase: SendAuthRequestUseCaseInterface, - private val respondAuthRequestUseCase: RespondAuthRequestUseCaseInterface, - private val formatMessageUseCase: FormatMessageUseCaseInterface, - private val decryptAuthMessageUseCase: DecryptMessageUseCaseInterface, - private val getVerifyContextUseCase: GetVerifyContextUseCaseInterface, - private val getListOfVerifyContextsUseCase: GetListOfVerifyContextsUseCaseInterface, - private val onAuthRequestUseCase: OnAuthRequestUseCase, - private val onAuthRequestResponseUseCase: OnAuthRequestResponseUseCase, -) : SendAuthRequestUseCaseInterface by sendAuthRequestUseCase, - RespondAuthRequestUseCaseInterface by respondAuthRequestUseCase, - FormatMessageUseCaseInterface by formatMessageUseCase, - DecryptMessageUseCaseInterface by decryptAuthMessageUseCase, - GetPendingJsonRpcHistoryEntriesUseCaseInterface by getPendingJsonRpcHistoryEntriesUseCase, - GetVerifyContextUseCaseInterface by getVerifyContextUseCase, - GetListOfVerifyContextsUseCaseInterface by getListOfVerifyContextsUseCase { - private var jsonRpcRequestsJob: Job? = null - private var jsonRpcResponsesJob: Job? = null - private var internalErrorsJob: Job? = null - private var authEventsJob: Job? = null - - private val _engineEvent: MutableSharedFlow = MutableSharedFlow() - val engineEvent: SharedFlow = _engineEvent.asSharedFlow() - - init { - pairingHandler.register(JsonRpcMethod.WC_AUTH_REQUEST) - emitReceivedAuthRequest() - } - - fun setup() { - jsonRpcInteractor.wssConnectionState - .filterIsInstance() - .onEach { - supervisorScope { - launch(Dispatchers.IO) { resubscribeToPendingRequestsTopics() } - } - - if (jsonRpcRequestsJob == null) { - jsonRpcRequestsJob = collectJsonRpcRequests() - } - if (jsonRpcResponsesJob == null) { - jsonRpcResponsesJob = collectJsonRpcResponses() - } - if (internalErrorsJob == null) { - internalErrorsJob = collectInternalErrors() - } - if (authEventsJob == null) { - authEventsJob = collectAuthEvents() - } - } - .launchIn(scope) - } - - private fun collectJsonRpcRequests(): Job = - jsonRpcInteractor.clientSyncJsonRpc - .filter { request -> request.params is AuthParams.RequestParams } - .onEach { request -> onAuthRequestUseCase(request, request.params as AuthParams.RequestParams) } - .launchIn(scope) - - private fun collectJsonRpcResponses(): Job = - jsonRpcInteractor.peerResponse - .filter { response -> response.params is AuthParams } - .onEach { response -> onAuthRequestResponseUseCase(response, response.params as AuthParams.RequestParams) } - .launchIn(scope) - - private fun resubscribeToPendingRequestsTopics() { - val responseTopics = pairingTopicToResponseTopicMap.map { (_, responseTopic) -> responseTopic.value } - try { - jsonRpcInteractor.batchSubscribe(responseTopics) { error -> scope.launch { _engineEvent.emit(SDKError(error)) } } - } catch (e: Exception) { - scope.launch { _engineEvent.emit(SDKError(e)) } - } - } - - private fun emitReceivedAuthRequest() { - pairingHandler.storedPairingFlow - .onEach { (pairingTopic, trace) -> - try { - val request = getPendingJsonRpcHistoryEntryByTopicUseCase(pairingTopic) - val context = verifyContextStorageRepository.get(request.id) ?: VerifyContext(request.id, String.Empty, Validation.UNKNOWN, String.Empty, null) - scope.launch { _engineEvent.emit(Events.OnAuthRequest(request.id, request.pairingTopic, request.payloadParams, context)) } - } catch (e: Exception) { - println("No auth request for pairing topic: $e") - } - }.launchIn(scope) - } - - private fun collectAuthEvents(): Job = - merge(sendAuthRequestUseCase.events, onAuthRequestUseCase.events, onAuthRequestResponseUseCase.events) - .onEach { event -> _engineEvent.emit(event) } - .launchIn(scope) - - private fun collectInternalErrors(): Job = - merge(jsonRpcInteractor.internalErrors, pairingHandler.findWrongMethodsFlow) - .onEach { exception -> _engineEvent.emit(exception) } - .launchIn(scope) -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/mapper/EngineMapper.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/mapper/EngineMapper.kt deleted file mode 100644 index 6d4b53f42..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/engine/mapper/EngineMapper.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.walletconnect.auth.engine.mapper - -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.auth.common.model.JsonRpcHistoryEntry -import com.walletconnect.auth.common.model.PayloadParams -import com.walletconnect.auth.common.model.PendingRequest - -@JvmSynthetic -internal fun PayloadParams.toCacaoPayload(iss: Issuer): Cacao.Payload = Cacao.Payload( - iss.value, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - iat = iat, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources -) - -@JvmSynthetic -internal fun PayloadParams.toCAIP222Message(iss: Issuer, chainName: String = "Ethereum"): String = - this.toCacaoPayload(iss).toCAIP222Message(chainName) - -@JvmSynthetic -internal fun JsonRpcHistoryEntry.toPendingRequest(): PendingRequest = PendingRequest(id, topic.value, params.payloadParams) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntriesUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntriesUseCase.kt deleted file mode 100644 index 9293ab443..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntriesUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.auth.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.auth.common.json_rpc.AuthRpc -import com.walletconnect.auth.common.model.PendingRequest -import com.walletconnect.auth.engine.mapper.toPendingRequest -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.auth.json_rpc.model.toEntry - -internal class GetPendingJsonRpcHistoryEntriesUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) : GetPendingJsonRpcHistoryEntriesUseCaseInterface { - - override suspend fun getPendingRequests(): List { - return jsonRpcHistory.getListOfPendingRecords() - .filter { record -> record.method == JsonRpcMethod.WC_AUTH_REQUEST } - .mapNotNull { record -> serializer.tryDeserialize(record.body)?.params?.toEntry(record) } - .map { jsonRpcHistoryEntry -> jsonRpcHistoryEntry.toPendingRequest() } - } -} - -internal interface GetPendingJsonRpcHistoryEntriesUseCaseInterface { - suspend fun getPendingRequests(): List -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt deleted file mode 100644 index 64eb3357f..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.auth.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.auth.common.json_rpc.AuthRpc -import com.walletconnect.auth.common.model.JsonRpcHistoryEntry -import com.walletconnect.auth.json_rpc.model.toEntry - -internal class GetPendingJsonRpcHistoryEntryByIdUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - - operator fun invoke(id: Long): JsonRpcHistoryEntry? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) - var entry: JsonRpcHistoryEntry? = null - - if (record != null) { - val authRequest: AuthRpc.AuthRequest? = serializer.tryDeserialize(record.body) - if (authRequest != null) { - entry = record.toEntry(authRequest.params) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByTopicUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByTopicUseCase.kt deleted file mode 100644 index b189f4ed0..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/domain/GetPendingJsonRpcHistoryEntryByTopicUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.auth.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.auth.common.json_rpc.AuthRpc -import com.walletconnect.auth.common.model.PendingRequest -import com.walletconnect.auth.engine.mapper.toPendingRequest -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.auth.json_rpc.model.toEntry -import com.walletconnect.foundation.common.model.Topic - -internal class GetPendingJsonRpcHistoryEntryByTopicUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - - suspend operator fun invoke(topic: Topic): PendingRequest { - return jsonRpcHistory.getListOfPendingRecords() - .filter { record -> record.method == JsonRpcMethod.WC_AUTH_REQUEST && record.topic == topic.value } - .mapNotNull { record -> serializer.tryDeserialize(record.body)?.params?.toEntry(record) } - .map { jsonRpcHistoryEntry -> jsonRpcHistoryEntry.toPendingRequest() } - .first() - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMapper.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMapper.kt deleted file mode 100644 index ff2fd1e9d..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMapper.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.auth.json_rpc.model - -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.model.JsonRpcHistoryEntry -import com.walletconnect.foundation.common.model.Topic - -@JvmSynthetic -internal fun JsonRpcHistoryRecord.toEntry(params: AuthParams.RequestParams): JsonRpcHistoryEntry = - JsonRpcHistoryEntry( - id, - Topic(topic), - method, - params, - response - ) - -@JvmSynthetic -internal fun AuthParams.RequestParams.toEntry(record: JsonRpcHistoryRecord): JsonRpcHistoryEntry = - JsonRpcHistoryEntry( - record.id, - Topic(record.topic), - record.method, - this, - record.response - ) \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMethod.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMethod.kt deleted file mode 100644 index c9bc67d96..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/json_rpc/model/JsonRpcMethod.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.json_rpc.model - -internal object JsonRpcMethod { - @get:JvmSynthetic - const val WC_AUTH_REQUEST: String = "wc_authRequest" -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/signature/cacao/CacaoSigner.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/signature/cacao/CacaoSigner.kt deleted file mode 100644 index ab522fd05..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/signature/cacao/CacaoSigner.kt +++ /dev/null @@ -1,15 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.auth.signature.cacao - -import com.walletconnect.android.cacao.signature.ISignatureType -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.auth.client.Auth - -/// Note: Szymon - Only added to have backwards compatibility. Newer SDKs should only add CacaoSigner object below. -@Deprecated("Moved to android-core module, as other SDKs also need CACAO.", ReplaceWith("com.walletconnect.android.internal.common.cacao.signature.SignatureType")) -enum class SignatureType(override val header: String) : ISignatureType { - EIP191("eip191"), EIP1271("eip1271"); -} - -object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/DecryptAuthMessageUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/DecryptAuthMessageUseCase.kt deleted file mode 100644 index c3a65e1d3..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/DecryptAuthMessageUseCase.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.android.utils.toClient -import com.walletconnect.auth.common.exceptions.InvalidAuthParamsType -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.foundation.common.model.Topic -import org.bouncycastle.util.encoders.Base64 - -class DecryptAuthMessageUseCase( - private val codec: Codec, - private val serializer: JsonRpcSerializer, - private val metadataRepository: MetadataStorageRepositoryInterface, - private val pushMessageStorageRepository: PushMessagesRepository -) : DecryptMessageUseCaseInterface { - override suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) { - try { - if (!pushMessageStorageRepository.doesPushMessageExist(sha256(message.toByteArray()))) { - val decryptedMessageString = codec.decrypt(Topic(topic), Base64.decode(message)) - val clientJsonRpc: ClientJsonRpc = serializer.tryDeserialize(decryptedMessageString) ?: return onFailure(InvalidAuthParamsType()) - val params: ClientParams = serializer.deserialize(clientJsonRpc.method, decryptedMessageString) ?: return onFailure(InvalidAuthParamsType()) - val metadata: AppMetaData = metadataRepository.getByTopicAndType(Topic(topic), AppMetaDataType.PEER) ?: return onFailure(InvalidAuthParamsType()) - - if (params is AuthParams.RequestParams) { - onSuccess(params.toMessage(clientJsonRpc.id, topic, metadata)) - } else { - onFailure(InvalidAuthParamsType()) - } - } - } catch (e: Exception) { - onFailure(e) - } - } - - private fun AuthParams.RequestParams.toMessage(id: Long, topic: String, metadata: AppMetaData): Core.Model.Message.AuthRequest = with(payloadParams) { - Core.Model.Message.AuthRequest( - id, - topic, - metadata.toClient(), - Core.Model.Message.AuthRequest.PayloadParams(type, chainId, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) - ) - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/FormatMessageUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/FormatMessageUseCase.kt deleted file mode 100644 index 9064fddb0..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/FormatMessageUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.auth.common.exceptions.InvalidParamsException -import com.walletconnect.auth.common.model.PayloadParams -import com.walletconnect.auth.engine.mapper.toCAIP222Message -import kotlinx.coroutines.supervisorScope - -internal class FormatMessageUseCase : FormatMessageUseCaseInterface { - override suspend fun formatMessage(payloadParams: PayloadParams, iss: String): String = supervisorScope { - val issuer = Issuer(iss) - if (issuer.chainId != payloadParams.chainId) throw InvalidParamsException("Issuer chainId does not match with PayloadParams") - if (!CoreValidator.isChainIdCAIP2Compliant(payloadParams.chainId)) throw InvalidParamsException("PayloadParams chainId is not CAIP-2 compliant") - if (!CoreValidator.isChainIdCAIP2Compliant(issuer.chainId)) throw InvalidParamsException("Issuer chainId is not CAIP-2 compliant") - if (!CoreValidator.isAccountIdCAIP10Compliant(issuer.accountId)) throw InvalidParamsException("Issuer address is not CAIP-10 compliant") - - return@supervisorScope payloadParams.toCAIP222Message(issuer) - } -} - -internal interface FormatMessageUseCaseInterface { - suspend fun formatMessage(payloadParams: PayloadParams, iss: String): String -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetListOfVerifyContextsUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetListOfVerifyContextsUseCase.kt deleted file mode 100644 index 2076b97a6..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetListOfVerifyContextsUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.verify.model.VerifyContext -import kotlinx.coroutines.supervisorScope - -internal class GetListOfVerifyContextsUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetListOfVerifyContextsUseCaseInterface { - override suspend fun getListOfVerifyContext(): List = supervisorScope { verifyContextStorageRepository.getAll() } -} - -internal interface GetListOfVerifyContextsUseCaseInterface { - suspend fun getListOfVerifyContext(): List -} diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetVerifyContextUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetVerifyContextUseCase.kt deleted file mode 100644 index f305ec7f3..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/GetVerifyContextUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.verify.model.VerifyContext -import kotlinx.coroutines.supervisorScope - -internal class GetVerifyContextUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetVerifyContextUseCaseInterface { - override suspend fun getVerifyContext(id: Long): VerifyContext? = supervisorScope { verifyContextStorageRepository.get(id) } -} - -internal interface GetVerifyContextUseCaseInterface { - suspend fun getVerifyContext(id: Long): VerifyContext? -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/RespondAuthRequestUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/RespondAuthRequestUseCase.kt deleted file mode 100644 index 6971da432..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/RespondAuthRequestUseCase.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.Invalid -import com.walletconnect.android.internal.common.exception.InvalidExpiryException -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.params.CoreAuthParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.auth.client.mapper.toCommon -import com.walletconnect.auth.common.exceptions.InvalidCacaoException -import com.walletconnect.auth.common.exceptions.MissingAuthRequestException -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.model.Respond -import com.walletconnect.auth.engine.mapper.toCacaoPayload -import com.walletconnect.auth.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.auth.json_rpc.model.JsonRpcMethod -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class RespondAuthRequestUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val getPendingJsonRpcHistoryEntryByIdUseCase: GetPendingJsonRpcHistoryEntryByIdUseCase, - private val crypto: KeyManagementRepository, - private val cacaoVerifier: CacaoVerifier, - private val verifyContextStorageRepository: VerifyContextStorageRepository, - private val logger: Logger, - private val pairingController: PairingControllerInterface -) : RespondAuthRequestUseCaseInterface { - - override suspend fun respond(respond: Respond, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - val jsonRpcHistoryEntry = getPendingJsonRpcHistoryEntryByIdUseCase(respond.id) - - if (jsonRpcHistoryEntry == null) { - logger.error(MissingAuthRequestException.message) - onFailure(MissingAuthRequestException) - return@supervisorScope - } - - val authParams: AuthParams.RequestParams = jsonRpcHistoryEntry.params - val response: JsonRpcResponse = handleResponse(respond, authParams) - - val receiverPublicKey = PublicKey(authParams.requester.publicKey) - val senderPublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() - val symmetricKey: SymmetricKey = crypto.generateSymmetricKeyFromKeyAgreement(senderPublicKey, receiverPublicKey) - val responseTopic: Topic = crypto.getTopicFromKey(receiverPublicKey) - - authParams.expiry?.let { expiry -> - if (checkExpiry(expiry, responseTopic, respond, authParams)) return@supervisorScope onFailure(InvalidExpiryException()) - } - - crypto.setKey(symmetricKey, responseTopic.value) - val irnParams = IrnParams(Tags.AUTH_REQUEST_RESPONSE, Ttl(dayInSeconds), false) - jsonRpcInteractor.publishJsonRpcResponse( - responseTopic, irnParams, response, envelopeType = EnvelopeType.ONE, participants = Participants(senderPublicKey, receiverPublicKey), - onSuccess = { - logger.log("Success Responded on topic: $responseTopic") - scope.launch { - supervisorScope { - verifyContextStorageRepository.delete(respond.id) - } - } - onSuccess() - }, - onFailure = { error -> - logger.error("Error Responded on topic: $responseTopic") - scope.launch { - supervisorScope { - verifyContextStorageRepository.delete(respond.id) - } - } - onFailure(error) - } - ) - } - - private fun checkExpiry(expiry: Expiry, responseTopic: Topic, respond: Respond, authParams: AuthParams.RequestParams): Boolean { - if (expiry.isExpired()) { - val irnParams = IrnParams(Tags.AUTH_REQUEST_RESPONSE, Ttl(dayInSeconds)) - val wcRequest = WCRequest(responseTopic, respond.id, JsonRpcMethod.WC_AUTH_REQUEST, authParams, transportType = TransportType.RELAY) - jsonRpcInteractor.respondWithError(wcRequest, Invalid.RequestExpired, irnParams) - return true - } - return false - } - - private fun handleResponse(respond: Respond, authParams: AuthParams.RequestParams) = when (respond) { - is Respond.Error -> JsonRpcResponse.JsonRpcError(respond.id, error = JsonRpcResponse.Error(respond.code, respond.message)) - is Respond.Result -> { - val issuer = Issuer(respond.iss) - val payload: Cacao.Payload = authParams.payloadParams.toCacaoPayload(issuer) - val cacao = Cacao(CacaoType.EIP4361.toHeader(), payload, respond.signature.toCommon()) - val responseParams = CoreAuthParams.ResponseParams(cacao.header, cacao.payload, cacao.signature) - if (!cacaoVerifier.verify(cacao)) throw InvalidCacaoException - JsonRpcResponse.JsonRpcResult(respond.id, result = responseParams) - } - } -} - -internal interface RespondAuthRequestUseCaseInterface { - suspend fun respond(respond: Respond, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/SendAuthRequestUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/SendAuthRequestUseCase.kt deleted file mode 100644 index 0b9986e96..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/calls/SendAuthRequestUseCase.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.walletconnect.auth.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.InvalidExpiryException -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.json_rpc.AuthRpc -import com.walletconnect.auth.common.model.PayloadParams -import com.walletconnect.auth.common.model.Requester -import com.walletconnect.auth.engine.pairingTopicToResponseTopicMap -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import java.util.Date -import java.util.concurrent.TimeUnit - -internal class SendAuthRequestUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val crypto: KeyManagementRepository, - private val selfAppMetaData: AppMetaData, - private val logger: Logger -) : SendAuthRequestUseCaseInterface { - private val _events: MutableSharedFlow = MutableSharedFlow() - override val events: SharedFlow = _events.asSharedFlow() - - override suspend fun request(payloadParams: PayloadParams, expiry: Expiry?, topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - val nowInSeconds = TimeUnit.SECONDS.convert(Date().time, TimeUnit.SECONDS) - if (!CoreValidator.isExpiryWithinBounds(expiry)) { - return@supervisorScope onFailure(InvalidExpiryException()) - } - - val responsePublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() - val responseTopic: Topic = crypto.getTopicFromKey(responsePublicKey) - val authParams: AuthParams.RequestParams = AuthParams.RequestParams(Requester(responsePublicKey.keyAsHex, selfAppMetaData), payloadParams, expiry) - val authRequest: AuthRpc.AuthRequest = AuthRpc.AuthRequest(params = authParams) - val irnParamsTtl = getIrnParamsTtl(expiry, nowInSeconds) - val irnParams = IrnParams(Tags.AUTH_REQUEST, irnParamsTtl, true) - val pairingTopic = Topic(topic) - val requestTtlInSeconds = expiry?.run { seconds - nowInSeconds } ?: dayInSeconds - crypto.setKey(responsePublicKey, responseTopic.getParticipantTag()) - - jsonRpcInteractor.publishJsonRpcRequest(pairingTopic, irnParams, authRequest, - onSuccess = { - try { - jsonRpcInteractor.subscribe(responseTopic) { error -> return@subscribe onFailure(error) } - } catch (e: Exception) { - return@publishJsonRpcRequest onFailure(e) - } - - pairingTopicToResponseTopicMap[pairingTopic] = responseTopic - onSuccess() - collectPeerResponse(requestTtlInSeconds, authRequest) - }, - onFailure = { error -> - logger.error("Failed to send a auth request: $error") - onFailure(error) - } - ) - } - - private fun getIrnParamsTtl(expiry: Expiry?, nowInSeconds: Long) = expiry?.run { - val defaultTtl = dayInSeconds - val extractedTtl = seconds - nowInSeconds - val newTtl = extractedTtl.takeIf { extractedTtl >= defaultTtl } ?: defaultTtl - Ttl(newTtl) - } ?: Ttl(dayInSeconds) - - private fun collectPeerResponse(requestTtlInSeconds: Long, authRequest: AuthRpc.AuthRequest) { - scope.launch { - try { - withTimeout(TimeUnit.SECONDS.toMillis(requestTtlInSeconds)) { - jsonRpcInteractor.peerResponse - .filter { response -> response.response.id == authRequest.id } - .collect { cancel() } - } - } catch (e: TimeoutCancellationException) { - _events.emit(SDKError(e)) - } - } - } -} - -internal interface SendAuthRequestUseCaseInterface { - val events: SharedFlow - suspend fun request(payloadParams: PayloadParams, expiry: Expiry? = null, topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/requests/OnAuthRequestUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/requests/OnAuthRequestUseCase.kt deleted file mode 100644 index b1e076668..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/requests/OnAuthRequestUseCase.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.walletconnect.auth.use_case.requests - -import com.walletconnect.android.internal.common.exception.Invalid -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.model.Events -import com.walletconnect.foundation.common.model.Ttl -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class OnAuthRequestUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, - private val pairingController: PairingControllerInterface, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcRequest: WCRequest, authParams: AuthParams.RequestParams) = supervisorScope { - val irnParams = IrnParams(Tags.AUTH_REQUEST_RESPONSE, Ttl(dayInSeconds)) - try { - authParams.expiry?.let { - if (it.isExpired()) { - jsonRpcInteractor.respondWithError(wcRequest, Invalid.RequestExpired, irnParams) - return@supervisorScope - } - } - - val url = authParams.requester.metadata.url - resolveAttestationIdUseCase(wcRequest, url) { verifyContext -> - scope.launch { _events.emit(Events.OnAuthRequest(wcRequest.id, wcRequest.topic.value, authParams.payloadParams, verifyContext)) } - } - } catch (e: Exception) { - jsonRpcInteractor.respondWithError(wcRequest, Uncategorized.GenericError("Cannot handle a auth request: ${e.message}, topic: ${wcRequest.topic}"), irnParams) - _events.emit(SDKError(e)) - } - } -} \ No newline at end of file diff --git a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/responses/OnAuthRequestResponseUseCase.kt b/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/responses/OnAuthRequestResponseUseCase.kt deleted file mode 100644 index 456a43a24..000000000 --- a/protocol/auth/src/main/kotlin/com/walletconnect/auth/use_case/responses/OnAuthRequestResponseUseCase.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.walletconnect.auth.use_case.responses - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.CoreAuthParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.utils.toClient -import com.walletconnect.auth.common.exceptions.PeerError -import com.walletconnect.auth.common.json_rpc.AuthParams -import com.walletconnect.auth.common.model.AuthResponse -import com.walletconnect.auth.common.model.Events -import com.walletconnect.auth.engine.pairingTopicToResponseTopicMap -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnAuthRequestResponseUseCase( - private val pairingInterface: PairingInterface, - private val pairingHandler: PairingControllerInterface, - private val cacaoVerifier: CacaoVerifier, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, requestParams: AuthParams.RequestParams) = supervisorScope { - try { - val pairingTopic = wcResponse.topic - if (!pairingInterface.getPairings().any { pairing -> pairing.topic == pairingTopic.value }) return@supervisorScope - pairingTopicToResponseTopicMap.remove(pairingTopic) - - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcError -> _events.emit(Events.OnAuthResponse(response.id, AuthResponse.Error(response.error.code, response.error.message))) - is JsonRpcResponse.JsonRpcResult -> { - pairingHandler.updateMetadata(Core.Params.UpdateMetadata(pairingTopic.value, requestParams.requester.metadata.toClient(), AppMetaDataType.PEER)) - val (header, payload, signature) = (response.result as CoreAuthParams.ResponseParams) - val cacao = Cacao(header, payload, signature) - if (cacaoVerifier.verify(cacao)) { - _events.emit(Events.OnAuthResponse(response.id, AuthResponse.Result(cacao))) - } else { - _events.emit(Events.OnAuthResponse(response.id, AuthResponse.Error(PeerError.SignatureVerificationFailed.code, PeerError.SignatureVerificationFailed.message))) - } - } - } - } catch (e: Exception) { - _events.emit(SDKError(e)) - } - } -} \ No newline at end of file diff --git a/protocol/auth/src/test/kotlin/com/walletconnect/auth/client/mapper/ClientMapperTest.kt b/protocol/auth/src/test/kotlin/com/walletconnect/auth/client/mapper/ClientMapperTest.kt deleted file mode 100644 index 82b865a48..000000000 --- a/protocol/auth/src/test/kotlin/com/walletconnect/auth/client/mapper/ClientMapperTest.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.walletconnect.auth.client.mapper - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.ISO_8601_PATTERN -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.auth.client.Auth -import com.walletconnect.auth.engine.mapper.toCacaoPayload -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertTrue -import org.junit.Test -import java.text.SimpleDateFormat -import java.time.Clock -import java.time.Duration -import java.time.Instant - -internal class ClientMapperTest { - private val iss = "did:pkh:eip155:1:0x15bca56b6e2728aec2532df9d436bd1600e86688" - private val dummyPairing = Core.Model.Pairing("", 0L, null, "", null, "", true, "") - - private fun Cacao.Payload.mockIatAsNbf(request: Auth.Params.Request): Cacao.Payload { - return this.copy(iat = request.nbf!!) - } - - private fun Auth.Params.Request.toCacaoPayload(iss: String): Cacao.Payload = this.toCommon().toCacaoPayload(Issuer(iss)) - - @Test - fun `Payload based on Request mapping with supplied issuer`() { - val request = Auth.Params.Request( - topic = dummyPairing.topic, - type = "eip191", - chainId = "eip155:1", - domain = "service.invalid", - aud = "https://service.invalid/login", - nonce = "32891756", - nbf = "2021-09-30T16:25:24Z", - exp = null, - statement = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", - requestId = null, - resources = listOf("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json") - ) - - val payload = Cacao.Payload( - iss = iss, - domain = "service.invalid", - aud = "https://service.invalid/login", - version = "1", - nonce = "32891756", - iat = "2021-09-30T16:25:24Z", - nbf = "2021-09-30T16:25:24Z", - exp = null, - statement = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", - requestId = null, - resources = listOf("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json") - ) - - assertEquals(payload, request.toCacaoPayload(iss).mockIatAsNbf(request)) - } - - @Test - fun `Payload based on Request generates issued at with current time`() { - val before = Instant.now(Clock.offset(Clock.systemDefaultZone(), Duration.ofSeconds(-2))) - - val payload = Auth.Params.Request( - topic = dummyPairing.topic, - type = "eip191", - chainId = "eip155:1", - domain = "service.invalid", - aud = "https://service.invalid/login", - nonce = "32891756", - nbf = "2021-09-30T16:25:24Z", - exp = null, - statement = "I accept the ServiceOrg Terms of Service: https://service.invalid/tos", - requestId = null, - resources = listOf("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/", "https://example.com/my-web2-claim.json") - ).toCacaoPayload(iss) - - val sd = SimpleDateFormat(ISO_8601_PATTERN).parse(payload.iat) - val iat = Instant.ofEpochMilli(sd!!.time) - val isAfter = Instant.now().isAfter(iat) - val isBefore = before.isBefore(iat) - assertTrue(isAfter) - assertTrue(isBefore) - } -} \ No newline at end of file diff --git a/protocol/chat/ReadMe.md b/protocol/chat/ReadMe.md deleted file mode 100644 index 332a652b9..000000000 --- a/protocol/chat/ReadMe.md +++ /dev/null @@ -1,45 +0,0 @@ -# **WalletConnect Chat - Kotlin** - -Kotlin implementation of WalletConnect v2 Chat protocol for Android applications. - -![Maven Central](https://img.shields.io/maven-central/v/com.walletconnect/chat) - -## Requirements - -* Android min SDK 23 -* Java 11 - -## Documentation and usage - -* [Installation guide](https://docs.walletconnect.com/2.0/kotlin/chat/installation) -* [Usage](https://docs.walletconnect.com/2.0/kotlin/chat/usage) -* [Protocol specification](https://docs.walletconnect.com/2.0/specs/chat/) - -  - -## Installation - -root/build.gradle.kts: - -```gradle -allprojects { - repositories { - mavenCentral() - maven { url "https://jitpack.io" } - } -} -``` - -app/build.gradle.kts - -```gradle -implementation(platform("com.walletconnect:android-bom:{BOM version}")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:chat") -``` - -  - -## Sample apps - -* For sample app run `chat sample module` diff --git a/protocol/chat/build.gradle.kts b/protocol/chat/build.gradle.kts deleted file mode 100644 index 28565b9e8..000000000 --- a/protocol/chat/build.gradle.kts +++ /dev/null @@ -1,79 +0,0 @@ -plugins { - id(libs.plugins.android.library.get().pluginId) - id(libs.plugins.kotlin.android.get().pluginId) - alias(libs.plugins.sqlDelight) - alias(libs.plugins.google.ksp) - id("publish-module-android") - id("jacoco-report") -} - -project.apply { - extra[KEY_PUBLISH_ARTIFACT_ID] = CHAT - extra[KEY_PUBLISH_VERSION] = CHAT_VERSION - extra[KEY_SDK_NAME] = "Chat" -} - -android { - namespace = "com.walletconnect.chat" - compileSdk = COMPILE_SDK - - defaultConfig { - minSdk = MIN_SDK - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "${rootDir.path}/gradle/proguard-rules/sdk-rules.pro") - } - } - lint { - abortOnError = true - ignoreWarnings = true - warningsAsErrors = false - } - - compileOptions { - sourceCompatibility = jvmVersion - targetCompatibility = jvmVersion - } - - kotlinOptions { - jvmTarget = jvmVersion.toString() - } - - testOptions.unitTests { - isIncludeAndroidResources = true - isReturnDefaultValues = true - } -} - -sqldelight { - databases { - create("ChatDatabase") { - packageName.set("com.walletconnect.chat") - schemaOutputDirectory.set(file("src/main/sqldelight/databases")) -// generateAsync.set(true) // TODO: Enable once all repository methods have been converted to suspend functions - verifyMigrations.set(true) - verifyDefinitions.set(true) - } - } -} - -dependencies { - debugImplementation(project(":core:android")) - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") - - implementation(libs.bundles.retrofit) - ksp(libs.moshi.ksp) - implementation(libs.bundles.sqlDelight) - - testImplementation(libs.bundles.androidxTest) - testImplementation(libs.robolectric) - testImplementation(libs.json) - testImplementation(libs.coroutines.test) - testImplementation(libs.bundles.scarlet.test) - testImplementation(libs.bundles.sqlDelight.test) -} \ No newline at end of file diff --git a/protocol/chat/proguard-rules.pro b/protocol/chat/proguard-rules.pro deleted file mode 100644 index 6dc836563..000000000 --- a/protocol/chat/proguard-rules.pro +++ /dev/null @@ -1 +0,0 @@ --keep public class com.walletconnect.chat.** { *; } \ No newline at end of file diff --git a/protocol/chat/src/debug/sqldelight/databases/1.db b/protocol/chat/src/debug/sqldelight/databases/1.db deleted file mode 100644 index fc7125d13..000000000 Binary files a/protocol/chat/src/debug/sqldelight/databases/1.db and /dev/null differ diff --git a/protocol/chat/src/main/AndroidManifest.xml b/protocol/chat/src/main/AndroidManifest.xml deleted file mode 100644 index 63eafa80a..000000000 --- a/protocol/chat/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/cacao/CacaoSigner.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/cacao/CacaoSigner.kt deleted file mode 100644 index 09fbd2869..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/cacao/CacaoSigner.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.chat.cacao - -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.chat.client.Chat - -object CacaoSigner : CacaoSignerInterface diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/Chat.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/Chat.kt deleted file mode 100644 index d1447f951..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/Chat.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.walletconnect.chat.client - -import androidx.annotation.Keep -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface - -object Chat { - sealed interface Listeners { - fun onError(error: Model.Error) - - interface PublicKeyOnSuccess : Listeners { - fun onSuccess(publicKey: String) - } - - interface SignMessage : Listeners { - fun onSign(message: String): Model.Cacao.Signature? - } - - interface Resolve : PublicKeyOnSuccess - interface Register : PublicKeyOnSuccess, SignMessage - interface Unregister : PublicKeyOnSuccess - } - - sealed interface Type { - enum class InviteStatus : Type { PENDING, REJECTED, APPROVED } - - @JvmInline - value class AccountId(val value: String) : Type - - @JvmInline - value class InviteMessage(val value: String) : Type - - @JvmInline - value class MediaData(val value: String) : Type - - @JvmInline - value class ChatMessage(val value: String) : Type - } - - sealed class Model { - data class Error(val throwable: Throwable) : Model() - - data class ConnectionState(val isAvailable: Boolean) : Model() - - sealed interface Invite { - val id: Long - val inviterAccount: Type.AccountId - val inviteeAccount: Type.AccountId - val message: Type.InviteMessage - val inviterPublicKey: String - val status: Type.InviteStatus - - data class Received( - override val id: Long, - override val inviterAccount: Type.AccountId, - override val inviteeAccount: Type.AccountId, - override val message: Type.InviteMessage, - override val inviterPublicKey: String, - override val status: Type.InviteStatus, - ) : Invite - - data class Sent( - override val id: Long, - override val inviterAccount: Type.AccountId, - override val inviteeAccount: Type.AccountId, - override val message: Type.InviteMessage, - override val inviterPublicKey: String, - override val status: Type.InviteStatus, - ) : Invite - } - - data class Media( - val type: String, - val data: Type.MediaData, - ) : Model() - - data class Thread( - val topic: String, - val selfAccount: Type.AccountId, - val peerAccount: Type.AccountId, - ) : Model() - - data class Message( - val topic: String, - val message: Type.ChatMessage, - val authorAccount: Type.AccountId, - val timestamp: Long, - val media: Media?, - ) : Model() - - sealed class Events : Model() { - data class OnInvite(val invite: Invite.Received) : Events() - data class OnInviteAccepted(val topic: String, val invite: Invite.Sent) : Events() - data class OnInviteRejected(val invite: Invite.Sent) : Events() - data class OnMessage(val message: Message) : Events() - data class OnLeft(val topic: String) : Events() - } - - data class Cacao( - val header: Header, - val payload: Payload, - val signature: Signature, - ) : Model() { - @Keep - data class Signature(override val t: String, override val s: String, override val m: String? = null) : Model(), SignatureInterface - data class Header(val t: String) : Model() - data class Payload( - val iss: String, - val domain: String, - val aud: String, - val version: String, - val nonce: String, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - val resources: List?, - ) : Model() - } - } - - sealed class Params { - data class Init(val core: CoreInterface) : Params() - data class Resolve(val account: Type.AccountId) : Params() - data class Invite(val inviterAccount: Type.AccountId, val inviteeAccount: Type.AccountId, val message: Type.InviteMessage, val inviteePublicKey: String) : Params() - data class Accept(val inviteId: Long) : Params() - data class Reject(val inviteId: Long) : Params() - data class Message(val topic: String, val message: Type.ChatMessage, val media: Model.Media? = null) : Params() - data class Ping(val topic: String) : Params() - data class Leave(val topic: String) : Params() - data class GetReceivedInvites(val account: Type.AccountId) : Params() - data class GetSentInvites(val account: Type.AccountId) : Params() - data class GetThreads(val account: Type.AccountId) : Params() - data class GetMessages(val topic: String) : Params() - data class Register(val account: Type.AccountId, val private: Boolean = false) : Params() - data class Unregister(val account: Type.AccountId) : Params() - data class GoPrivate(val account: Type.AccountId) : Params() - data class GoPublic(val account: Type.AccountId) : Params() - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatClient.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatClient.kt deleted file mode 100644 index 09cf03136..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatClient.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.chat.client - -object ChatClient: ChatInterface by ChatProtocol.instance { - interface ChatDelegate: ChatInterface.ChatDelegate -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatInterface.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatInterface.kt deleted file mode 100644 index 2b31a6189..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatInterface.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.walletconnect.chat.client - -interface ChatInterface { - - interface ChatDelegate { - fun onInvite(onInvite: Chat.Model.Events.OnInvite) - fun onInviteAccepted(onInviteAccepted: Chat.Model.Events.OnInviteAccepted) - fun onInviteRejected(onInviteRejected: Chat.Model.Events.OnInviteRejected) - fun onMessage(onMessage: Chat.Model.Events.OnMessage) - fun onLeft(onLeft: Chat.Model.Events.OnLeft) - fun onConnectionStateChange(state: Chat.Model.ConnectionState) - fun onError(error: Chat.Model.Error) - } - - fun setChatDelegate(delegate: ChatDelegate) - - fun initialize(init: Chat.Params.Init, onError: (Chat.Model.Error) -> Unit) - fun register(register: Chat.Params.Register, listener: Chat.Listeners.Register) - fun unregister(unregister: Chat.Params.Unregister, listener: Chat.Listeners.Unregister) - fun resolve(resolve: Chat.Params.Resolve, listener: Chat.Listeners.Resolve) - fun goPrivate(goPrivate: Chat.Params.GoPrivate, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) - fun goPublic(goPublic: Chat.Params.GoPublic, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) - fun invite(invite: Chat.Params.Invite, onSuccess: (Long) -> Unit, onError: (Chat.Model.Error) -> Unit) - fun accept(accept: Chat.Params.Accept, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) - fun reject(reject: Chat.Params.Reject, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) - fun message(message: Chat.Params.Message, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) - fun ping(ping: Chat.Params.Ping, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) - fun leave(leave: Chat.Params.Leave, onError: (Chat.Model.Error) -> Unit) - fun getReceivedInvites(getReceivedInvites: Chat.Params.GetReceivedInvites): Map - fun getSentInvites(getSentInvites: Chat.Params.GetSentInvites): Map - fun getThreads(getThreads: Chat.Params.GetThreads): Map - fun getMessages(getMessages: Chat.Params.GetMessages): List -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatProtocol.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatProtocol.kt deleted file mode 100644 index 309dc2d6a..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/ChatProtocol.kt +++ /dev/null @@ -1,168 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.client - -import com.walletconnect.android.internal.common.di.DatabaseConfig -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.chat.client.mapper.toClient -import com.walletconnect.chat.client.mapper.toClientError -import com.walletconnect.chat.client.mapper.toCommon -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.di.* -import com.walletconnect.chat.engine.domain.ChatEngine -import kotlinx.coroutines.launch -import org.koin.core.KoinApplication - -internal class ChatProtocol(private val koinApp: KoinApplication = wcKoinApp) : ChatInterface { - private lateinit var chatEngine: ChatEngine - - companion object { - val instance = ChatProtocol() - } - - @Throws(IllegalStateException::class) - override fun initialize(init: Chat.Params.Init, onError: (Chat.Model.Error) -> Unit) { - try { - koinApp.run { - modules( - jsonRpcModule(), - storageModule(koinApp.koin.get().CHAT_SDK_DB_NAME), - engineModule(), - ) - } - - chatEngine = koinApp.koin.get() - chatEngine.setup() - } catch (e: Exception) { - onError(Chat.Model.Error(e)) - } - } - - @Throws(IllegalStateException::class) - override fun setChatDelegate(delegate: ChatInterface.ChatDelegate): Unit = wrapWithEngineInitializationCheck() { - scope.launch { - chatEngine.events.collect { event -> - when (event) { - is Events.OnInvite -> delegate.onInvite(event.toClient()) - is Events.OnInviteAccepted -> delegate.onInviteAccepted(event.toClient()) - is Events.OnInviteRejected -> delegate.onInviteRejected(event.toClient()) - is Events.OnMessage -> delegate.onMessage(event.toClient()) - is Events.OnLeft -> delegate.onLeft(event.toClient()) - is ConnectionState -> delegate.onConnectionStateChange(event.toClient()) - is SDKError -> delegate.onError(event.toClientError()) - } - } - } - } - - @Throws(IllegalStateException::class) - override fun resolve(resolve: Chat.Params.Resolve, listener: Chat.Listeners.Resolve) = protocolFunction(listener::onError) { - chatEngine.resolveAccount( - AccountId(resolve.account.value), - { publicKey -> listener.onSuccess(publicKey) }, - { throwable -> listener.onError(Chat.Model.Error(throwable)) } - ) - } - - @Throws(IllegalStateException::class) - override fun goPrivate(goPrivate: Chat.Params.GoPrivate, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.goPrivate(goPrivate.account.toCommon(), { onSuccess() }, { error -> onError(Chat.Model.Error(error)) }) - } - - @Throws(IllegalStateException::class) - override fun goPublic(goPublic: Chat.Params.GoPublic, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.goPublic(goPublic.account.toCommon(), { inviteKey -> onSuccess(inviteKey) }, { error -> onError(Chat.Model.Error(error)) }) - } - - @Throws(IllegalStateException::class) - override fun invite(invite: Chat.Params.Invite, onSuccess: (Long) -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - scope.launch { chatEngine.invite(invite.toCommon(), { inviteId -> onSuccess(inviteId) }, { error -> onError(Chat.Model.Error(error)) }) } - } - - @Throws(IllegalStateException::class) - override fun accept(accept: Chat.Params.Accept, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.accept(accept.inviteId, { threadTopic -> onSuccess(threadTopic) }, { error -> onError(Chat.Model.Error(error)) }) - } - - @Throws(IllegalStateException::class) - override fun reject(reject: Chat.Params.Reject, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.reject(reject.inviteId, onSuccess) { error -> onError(Chat.Model.Error(error)) } - } - - @Throws(IllegalStateException::class) - override fun message(message: Chat.Params.Message, onSuccess: () -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.message(message.topic, message.toCommon(), onSuccess) { error -> onError(Chat.Model.Error(error)) } - } - - @Throws(IllegalStateException::class) - override fun ping(ping: Chat.Params.Ping, onSuccess: (String) -> Unit, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.ping(ping.topic, onSuccess = { topic -> onSuccess(topic) }, { error -> onError(Chat.Model.Error(error)) }) - } - - @Throws(IllegalStateException::class) - override fun leave(leave: Chat.Params.Leave, onError: (Chat.Model.Error) -> Unit) = protocolFunction(onError) { - chatEngine.leave(leave.topic) { error -> onError(Chat.Model.Error(error)) } - } - - @Throws(IllegalStateException::class) - override fun getReceivedInvites(getReceivedInvites: Chat.Params.GetReceivedInvites): Map = wrapWithEngineInitializationCheck() { - chatEngine.getReceivedInvites(getReceivedInvites.account.value).mapValues { (_, invite) -> invite.toClient() } - } - - @Throws(IllegalStateException::class) - override fun getSentInvites(getSentInvites: Chat.Params.GetSentInvites): Map = wrapWithEngineInitializationCheck() { - chatEngine.getSentInvites(getSentInvites.account.value).mapValues { (_, invite) -> invite.toClient() } - } - - @Throws(IllegalStateException::class) - override fun getThreads(getThreads: Chat.Params.GetThreads): Map = wrapWithEngineInitializationCheck() { - chatEngine.getThreads(getThreads.account.value).mapValues { (_, thread) -> thread.toClient() } - } - - @Throws(IllegalStateException::class) - override fun getMessages(getMessages: Chat.Params.GetMessages): List = wrapWithEngineInitializationCheck() { - chatEngine.getMessages(getMessages.topic).map { message -> message.toClient() }.sortedBy { message -> message.timestamp } - } - - @Throws(IllegalStateException::class) - override fun register(register: Chat.Params.Register, listener: Chat.Listeners.Register) = protocolFunction(listener::onError) { - chatEngine.register( - register.account.toCommon(), - onSign = { message -> listener.onSign(message).toCommon() }, - { didKey -> listener.onSuccess(didKey) }, - { throwable -> listener.onError(Chat.Model.Error(throwable)) }, - register.private - ) - } - - @Throws(IllegalStateException::class) - override fun unregister(unregister: Chat.Params.Unregister, listener: Chat.Listeners.Unregister) = protocolFunction(listener::onError) { - chatEngine.unregister( - unregister.account.toCommon(), - { didKey -> listener.onSuccess(didKey) }, - { throwable -> listener.onError(Chat.Model.Error(throwable)) }, - ) - } - - - @Throws(IllegalStateException::class) - private fun wrapWithEngineInitializationCheck(block: () -> R): R { - check(::chatEngine.isInitialized) { - "ChatClient needs to be initialized first using the initialize function" - } - return block() - } - - private fun wrapWithRunCatching(onError: (Chat.Model.Error) -> Unit, block: () -> Unit) = runCatching(block).onFailure { error -> onError(Chat.Model.Error(error)) } - - @Throws(IllegalStateException::class) - private fun protocolFunction(onError: (Chat.Model.Error) -> Unit, block: () -> Unit) { - wrapWithEngineInitializationCheck() { - wrapWithRunCatching(onError) { block() } - } - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/mapper/ClientMapper.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/mapper/ClientMapper.kt deleted file mode 100644 index 4d27dd886..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/client/mapper/ClientMapper.kt +++ /dev/null @@ -1,103 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.client.mapper - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.chat.client.Chat -import com.walletconnect.chat.common.model.ChatMessage -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.common.model.InviteMessage -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.common.model.Media -import com.walletconnect.chat.common.model.MediaData -import com.walletconnect.chat.common.model.Message -import com.walletconnect.chat.common.model.SendInvite -import com.walletconnect.chat.common.model.SendMessage -import com.walletconnect.chat.common.model.Thread -import com.walletconnect.foundation.common.model.Topic - -@JvmSynthetic -internal fun Chat.Params.Invite.toCommon(): SendInvite = SendInvite(inviterAccount.toCommon(), inviteeAccount.toCommon(), message.toCommon(), inviteePublicKey) - -@JvmSynthetic -internal fun Chat.Params.Message.toCommon(): SendMessage = SendMessage(Topic(topic), message.toCommon(), media?.toCommon()) - -@JvmSynthetic -internal fun Chat.Model.Media.toCommon(): Media = Media(type, data.toCommon()) - -@JvmSynthetic -internal fun Events.OnInvite.toClient(): Chat.Model.Events.OnInvite = Chat.Model.Events.OnInvite(invite.toClient()) - - -@JvmSynthetic -internal fun Thread.toClient(): Chat.Model.Thread = Chat.Model.Thread(topic.value, selfAccount.toClient(), peerAccount.toClient()) - -@JvmSynthetic -internal fun InviteStatus.toClient(): Chat.Type.InviteStatus = when (this) { - InviteStatus.PENDING -> Chat.Type.InviteStatus.PENDING - InviteStatus.REJECTED -> Chat.Type.InviteStatus.REJECTED - InviteStatus.APPROVED -> Chat.Type.InviteStatus.APPROVED -} - -@JvmSynthetic -internal fun Invite.Received.toClient(): Chat.Model.Invite.Received = - Chat.Model.Invite.Received(id, inviterAccount.toClient(), inviteeAccount.toClient(), message.toClient(), inviterPublicKey.keyAsHex, status.toClient()) - -@JvmSynthetic -internal fun Invite.Sent.toClient(): Chat.Model.Invite.Sent = - Chat.Model.Invite.Sent(id, inviterAccount.toClient(), inviteeAccount.toClient(), message.toClient(), inviterPublicKey.keyAsHex, status.toClient()) - -@JvmSynthetic -internal fun AccountId.toClient(): Chat.Type.AccountId = Chat.Type.AccountId(value) - -@JvmSynthetic -internal fun ChatMessage.toClient(): Chat.Type.ChatMessage = Chat.Type.ChatMessage(value) - -@JvmSynthetic -internal fun InviteMessage.toClient(): Chat.Type.InviteMessage = Chat.Type.InviteMessage(value) - -@JvmSynthetic -internal fun MediaData.toClient(): Chat.Type.MediaData = Chat.Type.MediaData(value) - -@JvmSynthetic -internal fun Media.toClient(): Chat.Model.Media = Chat.Model.Media(type, data.toClient()) - -@JvmSynthetic -internal fun Events.OnInviteAccepted.toClient(): Chat.Model.Events.OnInviteAccepted = Chat.Model.Events.OnInviteAccepted(topic, invite.toClient()) - -@JvmSynthetic -internal fun Events.OnMessage.toClient(): Chat.Model.Events.OnMessage = Chat.Model.Events.OnMessage(message.toClient()) - -@JvmSynthetic -internal fun Events.OnLeft.toClient(): Chat.Model.Events.OnLeft = Chat.Model.Events.OnLeft(topic) - -@JvmSynthetic -internal fun Events.OnInviteRejected.toClient(): Chat.Model.Events.OnInviteRejected = Chat.Model.Events.OnInviteRejected(invite.toClient()) - -@JvmSynthetic -internal fun Message.toClient(): Chat.Model.Message = Chat.Model.Message(topic.value, message.toClient(), authorAccount.toClient(), timestamp, media?.toClient()) - -@JvmSynthetic -internal fun SDKError.toClientError(): Chat.Model.Error = Chat.Model.Error(this.exception) - -@JvmSynthetic -internal fun ConnectionState.toClient(): Chat.Model.ConnectionState = Chat.Model.ConnectionState(isAvailable) - -@JvmSynthetic -internal fun Chat.Model.Cacao.Signature?.toCommon(): Cacao.Signature? = this?.let { cacao -> Cacao.Signature(cacao.t, cacao.s, cacao.m) } - -@JvmSynthetic -internal fun Chat.Type.AccountId.toCommon(): AccountId = AccountId(value) - -@JvmSynthetic -internal fun Chat.Type.ChatMessage.toCommon(): ChatMessage = ChatMessage(value) - -@JvmSynthetic -internal fun Chat.Type.InviteMessage.toCommon(): InviteMessage = InviteMessage(value) - -@JvmSynthetic -internal fun Chat.Type.MediaData.toCommon(): MediaData = MediaData(value) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/ChatExceptions.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/ChatExceptions.kt deleted file mode 100644 index 4d88f6083..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/ChatExceptions.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.walletconnect.chat.common.exceptions - -import com.walletconnect.android.internal.common.exception.WalletConnectException -import com.walletconnect.chat.engine.domain.ChatValidator.MAX_LENGTH_CHAT_MESSAGE -import com.walletconnect.chat.engine.domain.ChatValidator.MAX_LENGTH_INVITE_MESSAGE -import com.walletconnect.chat.engine.domain.ChatValidator.MAX_LENGTH_MEDIA_DATA - -class InvalidAccountIdException(override val message: String?) : WalletConnectException(message) // todo: https://github.com/WalletConnect/WalletConnectKotlinV2/issues/768 -class InviteKeyNotFound(override val message: String?) : WalletConnectException(message) -object AccountsAlreadyHaveInviteException : WalletConnectException("Accounts already have invite") -object AccountsAlreadyHaveThreadException : WalletConnectException("Accounts already have thread") -class InviteMessageTooLongException : WalletConnectException("Invite message max length is $MAX_LENGTH_INVITE_MESSAGE") -class ChatMessageTooLongException : WalletConnectException("Chat message max length is $MAX_LENGTH_CHAT_MESSAGE") -class MediaDataTooLongException : WalletConnectException("Media data max length is $MAX_LENGTH_MEDIA_DATA") -object MissingInviteRequestException : WalletConnectException("Missing Invite Request") -class InvalidActClaims(act: String) : WalletConnectException("Invalid act claim. Must be equal to $act") -object InviteWasAlreadyRespondedTo : WalletConnectException("This invite request has already been responded to") -object InviteResponseWasAlreadyReceived : WalletConnectException("This invite response has already been received") -object ChatSyncStoresInitializationTimeoutException : WalletConnectException("Required Chat Stores initialization timeout") -object ReceivedInviteNotStored : WalletConnectException("Received Invite not stored") diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/PeerError.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/PeerError.kt deleted file mode 100644 index 24e56f941..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/exceptions/PeerError.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.chat.common.exceptions - -import com.walletconnect.android.internal.common.model.type.Error - -sealed class PeerError : Error { - - //TODO: Discuss error with team on later stage of development - data class UserRejectedInvitation(override val message: String) : PeerError() { - override val code: Int = 4001 - } -} diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatParams.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatParams.kt deleted file mode 100644 index 46c8ea360..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatParams.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.chat.common.json_rpc - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.ClientParams - -internal sealed class ChatParams : ClientParams { - - @JsonClass(generateAdapter = true) - internal data class InviteParams( - @Json(name = "inviteAuth") - val inviteAuth: String, - ) : ChatParams() - - @JsonClass(generateAdapter = true) - internal data class MessageParams( - @Json(name = "messageAuth") - val messageAuth: String, - ) : ChatParams() - - @Suppress("CanSealedSubClassBeObject") - internal class PingParams : ChatParams() - - @Suppress("CanSealedSubClassBeObject") - internal class LeaveParams : ChatParams() -} diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatRpc.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatRpc.kt deleted file mode 100644 index 57ae38eb2..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/json_rpc/ChatRpc.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.walletconnect.chat.common.json_rpc - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.chat.json_rpc.JsonRpcMethod -import com.walletconnect.util.generateId - -internal sealed class ChatRpc : JsonRpcClientSync { - - @JsonClass(generateAdapter = true) - internal data class ChatInvite( - @Json(name = "id") - override val id: Long = generateId(), - @Json(name = "jsonrpc") - override val jsonrpc: String = "2.0", - @Json(name = "method") - override val method: String = JsonRpcMethod.WC_CHAT_INVITE, - @Json(name = "params") - override val params: ChatParams.InviteParams, - ) : ChatRpc() - - @JsonClass(generateAdapter = true) - internal data class ChatMessage( - @Json(name = "id") - override val id: Long = generateId(), - @Json(name = "jsonrpc") - override val jsonrpc: String = "2.0", - @Json(name = "method") - override val method: String = JsonRpcMethod.WC_CHAT_MESSAGE, - @Json(name = "params") - override val params: ChatParams.MessageParams, - ) : ChatRpc() - - @JsonClass(generateAdapter = true) - internal data class ChatPing( - @Json(name = "id") - override val id: Long = generateId(), - @Json(name = "jsonrpc") - override val jsonrpc: String = "2.0", - @Json(name = "method") - override val method: String = JsonRpcMethod.WC_CHAT_PING, - @Json(name = "params") - override val params: ChatParams.PingParams, - ) : ChatRpc() - - @JsonClass(generateAdapter = true) - internal data class ChatLeave( - @Json(name = "id") - override val id: Long = generateId(), - @Json(name = "jsonrpc") - override val jsonrpc: String = "2.0", - @Json(name = "method") - override val method: String = JsonRpcMethod.WC_CHAT_LEAVE, - @Json(name = "params") - override val params: ChatParams.LeaveParams, - ) : ChatRpc() -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Account.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Account.kt deleted file mode 100644 index 8201f8232..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Account.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -internal data class Account( - val accountId: AccountId, - val publicIdentityKey: PublicKey, - val publicInviteKey: PublicKey?, - val inviteTopic: Topic?, -) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/ChatMessage.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/ChatMessage.kt deleted file mode 100644 index 0583d713b..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/ChatMessage.kt +++ /dev/null @@ -1,11 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.chat.engine.domain.ChatValidator - - -@JvmInline -internal value class ChatMessage(val value: String) { - fun isValid(): Boolean = ChatValidator.isChatMessageValid(value) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Contact.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Contact.kt deleted file mode 100644 index 4fd62863d..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Contact.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey - -internal data class Contact( - val accountId: AccountId, - val publicKey: PublicKey, - val displayName: String -) diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Events.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Events.kt deleted file mode 100644 index 0a4d64103..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Events.kt +++ /dev/null @@ -1,13 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal sealed class Events : EngineEvent { - data class OnInvite(val invite: Invite.Received) : Events() - data class OnInviteAccepted(val topic: String, val invite: Invite.Sent) : Events() - data class OnInviteRejected(val invite: Invite.Sent) : Events() - data class OnMessage(val message: Message) : Events() - data class OnLeft(val topic: String) : Events() -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Invite.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Invite.kt deleted file mode 100644 index e23e93ac3..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Invite.kt +++ /dev/null @@ -1,48 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -internal sealed interface Invite { - val id: Long - val inviterAccount: AccountId - val inviteeAccount: AccountId - val message: InviteMessage - val inviterPublicKey: PublicKey - val status: InviteStatus - val acceptTopic: Topic - val symmetricKey: SymmetricKey - val inviterPrivateKey: PrivateKey? - val timestamp: Long - - data class Received( - override val id: Long, - override val inviterAccount: AccountId, - override val inviteeAccount: AccountId, - override val message: InviteMessage, - override val inviterPublicKey: PublicKey, - override val status: InviteStatus, - override val acceptTopic: Topic, - override val symmetricKey: SymmetricKey, - override val inviterPrivateKey: PrivateKey?, - override val timestamp: Long, - ) : Invite - - data class Sent( - override val id: Long, - override val inviterAccount: AccountId, - override val inviteeAccount: AccountId, - override val message: InviteMessage, - override val inviterPublicKey: PublicKey, - override val status: InviteStatus, - override val acceptTopic: Topic, - override val symmetricKey: SymmetricKey, - override val inviterPrivateKey: PrivateKey?, - override val timestamp: Long, - ) : Invite -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteMessage.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteMessage.kt deleted file mode 100644 index a9df0a47a..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteMessage.kt +++ /dev/null @@ -1,10 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.chat.engine.domain.ChatValidator - -@JvmInline -internal value class InviteMessage(val value: String) { - fun isValid(): Boolean = ChatValidator.isInviteMessageValid(value) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteStatus.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteStatus.kt deleted file mode 100644 index a04d8102d..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteStatus.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.chat.common.model - -// Used in Invites.sq -enum class InviteStatus { PENDING, REJECTED, APPROVED } \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteType.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteType.kt deleted file mode 100644 index 9a13ef3f1..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/InviteType.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.walletconnect.chat.common.model - -// Used in Invites.sq -enum class InviteType { SENT, RECEIVED } \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/JsonRpcHistoryEntry.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/JsonRpcHistoryEntry.kt deleted file mode 100644 index 5dc29af78..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/JsonRpcHistoryEntry.kt +++ /dev/null @@ -1,22 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.foundation.common.model.Topic - -internal interface JsonRpcHistoryEntry { - val id: Long - val topic: Topic - val method: String - val response: String? - val params: ChatParams - - data class InviteRequest( - override val params: ChatParams.InviteParams, - override val id: Long, - override val topic: Topic, - override val method: String, - override val response: String?, - ) : JsonRpcHistoryEntry -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Media.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Media.kt deleted file mode 100644 index b648e896f..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Media.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -internal data class Media( - val type: String, - val data: MediaData, -) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/MediaData.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/MediaData.kt deleted file mode 100644 index 91b810b3c..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/MediaData.kt +++ /dev/null @@ -1,10 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.chat.engine.domain.ChatValidator - -@JvmInline -internal value class MediaData(val value: String) { - fun isValid(): Boolean = ChatValidator.isMediaDataValid(value) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Message.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Message.kt deleted file mode 100644 index d2cac3c05..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Message.kt +++ /dev/null @@ -1,15 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.Topic - -internal data class Message( - val messageId: Long, - val topic: Topic, - val message: ChatMessage, - val authorAccount: AccountId, - val timestamp: Long, // We might need additional deliveryTimestamp for ordering - val media: Media?, -) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendInvite.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendInvite.kt deleted file mode 100644 index 345181511..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendInvite.kt +++ /dev/null @@ -1,7 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId - -internal data class SendInvite(val inviterAccount: AccountId, val inviteeAccount: AccountId, val message: InviteMessage, val inviteePublicKey: String) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendMessage.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendMessage.kt deleted file mode 100644 index baa774eb6..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/SendMessage.kt +++ /dev/null @@ -1,11 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.foundation.common.model.Topic - -internal data class SendMessage( - val topic: Topic, - val message: ChatMessage, - val media: Media?, -) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Thread.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Thread.kt deleted file mode 100644 index 3c9b62984..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/common/model/Thread.kt +++ /dev/null @@ -1,12 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.Topic - -internal data class Thread( - val topic: Topic, - val selfAccount: AccountId, - val peerAccount: AccountId, -) \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/ChatDITags.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/ChatDITags.kt deleted file mode 100644 index 355062c80..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/ChatDITags.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.chat.di - -enum class ChatDITags { - COLUMN_ADAPTER_INVITE_TYPE, - COLUMN_ADAPTER_INVITE_STATUS, -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/EngineModule.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/EngineModule.kt deleted file mode 100644 index 81e6494d1..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/EngineModule.kt +++ /dev/null @@ -1,174 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.chat.engine.domain.ChatEngine -import com.walletconnect.chat.engine.use_case.SubscribeToChatTopicsUseCase -import com.walletconnect.chat.engine.use_case.calls.AcceptInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.GetMessagesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetReceivedInvitesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetSentInvitesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetThreadsUseCase -import com.walletconnect.chat.engine.use_case.calls.GoPrivateUseCase -import com.walletconnect.chat.engine.use_case.calls.GoPublicUseCase -import com.walletconnect.chat.engine.use_case.calls.LeaveThreadUseCase -import com.walletconnect.chat.engine.use_case.calls.RegisterIdentityUseCase -import com.walletconnect.chat.engine.use_case.calls.RejectInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.ResolveAccountUseCase -import com.walletconnect.chat.engine.use_case.calls.SendInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.SendMessageUseCase -import com.walletconnect.chat.engine.use_case.calls.SendPingUseCase -import com.walletconnect.chat.engine.use_case.calls.UnregisterIdentityUseCase -import com.walletconnect.chat.engine.use_case.requests.OnInviteRequestUseCase -import com.walletconnect.chat.engine.use_case.requests.OnLeaveRequestUseCase -import com.walletconnect.chat.engine.use_case.requests.OnMessageRequestUseCase -import com.walletconnect.chat.engine.use_case.responses.OnInviteResponseUseCase -import com.walletconnect.chat.engine.use_case.responses.OnLeaveResponseUseCase -import com.walletconnect.chat.engine.use_case.responses.OnMessageResponseUseCase -import com.walletconnect.chat.json_rpc.GetPendingJsonRpcHistoryEntryByIdUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun engineModule() = module { - single { GetPendingJsonRpcHistoryEntryByIdUseCase(get(), get()) } - - single { - AcceptInviteUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - getPendingJsonRpcHistoryEntryByIdUseCase = get(), - logger = get(), - invitesRepository = get(), - keyManagementRepository = get(), - identitiesInteractor = get(), - jsonRpcInteractor = get(), - threadsRepository = get(), - ) - } - - single { - RejectInviteUseCase( - getPendingJsonRpcHistoryEntryByIdUseCase = get(), - logger = get(), - invitesRepository = get(), - keyManagementRepository = get(), - jsonRpcInteractor = get(), - ) - } - - single { - RegisterIdentityUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - identitiesInteractor = get(), - accountsRepository = get(), - goPublicUseCase = get(), - ) - } - - single { - UnregisterIdentityUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - identitiesInteractor = get(), - accountsRepository = get(), - keyManagementRepository = get(), - jsonRpcInteractor = get(), - ) - } - - single { - GoPublicUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - identitiesInteractor = get(), - accountsRepository = get(), - keyManagementRepository = get(), - jsonRpcInteractor = get(), - registerInviteUseCase = get(), - ) - } - - single { - GoPrivateUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - identitiesInteractor = get(), - accountsRepository = get(), - keyManagementRepository = get(), - jsonRpcInteractor = get(), - unregisterInviteUseCase = get(), - ) - } - - single { - SendInviteUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - logger = get(), - invitesRepository = get(), - keyManagementRepository = get(), - identitiesInteractor = get(), - jsonRpcInteractor = get(), - contactRepository = get(), - threadsRepository = get() - ) - } - - single { - SendMessageUseCase( - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - logger = get(), - identitiesInteractor = get(), - jsonRpcInteractor = get(), - threadsRepository = get(), - messageRepository = get() - ) - } - - single { LeaveThreadUseCase(logger = get(), jsonRpcInteractor = get(), threadsRepository = get()) } - single { ResolveAccountUseCase(resolveInviteUseCase = get()) } - single { GetThreadsUseCase(get()) } - single { GetMessagesUseCase(get()) } - single { GetSentInvitesUseCase(get()) } - single { GetReceivedInvitesUseCase(get()) } - single { SendPingUseCase(logger = get(), jsonRpcInteractor = get()) } - single { OnMessageResponseUseCase(logger = get()) } - single { OnLeaveResponseUseCase(logger = get()) } - single { OnInviteRequestUseCase(logger = get(), identitiesInteractor = get(), accountsRepository = get(), invitesRepository = get(), keyManagementRepository = get(), threadsRepository = get()) } - single { OnMessageRequestUseCase(logger = get(), identitiesInteractor = get(), messageRepository = get(), keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), jsonRpcInteractor = get()) } - single { OnLeaveRequestUseCase( jsonRpcInteractor = get(), threadsRepository = get()) } - - single { - OnInviteResponseUseCase( - logger = get(), invitesRepository = get(), keyManagementRepository = get(), identitiesInteractor = get(), threadsRepository = get(), jsonRpcInteractor = get(), - ) - } - - single { SubscribeToChatTopicsUseCase(logger = get(), invitesRepository = get(), accountsRepository = get(), threadsRepository = get(), jsonRpcInteractor = get()) } - - single { - ChatEngine( - jsonRpcInteractor = get(), - pairingHandler = get(), - acceptInviteUseCase = get(), - rejectInviteUseCase = get(), - goPublicUseCase = get(), - goPrivateUseCase = get(), - registerIdentityUseCase = get(), - onInviteRequestUseCase = get(), - onMessageRequestUseCase = get(), - sendInviteUseCase = get(), - sendMessageUseCase = get(), - unregisterIdentityUseCase = get(), - resolveAccountUseCase = get(), - leaveThreadUseCase = get(), - sendPingUseCase = get(), - onInviteResponseUseCase = get(), - getThreadsUseCase = get(), - getMessagesUseCase = get(), - getReceivedInvitesUseCase = get(), - getSentInvitesUseCase = get(), - onLeaveRequestUseCase = get(), - onLeaveResponseUseCase = get(), - onMessageResponseUseCase = get(), - subscribeToChatTopicsUseCase = get(), - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/JsonRpcModule.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/JsonRpcModule.kt deleted file mode 100644 index b8cb1991a..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/JsonRpcModule.kt +++ /dev/null @@ -1,22 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.di - -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.chat.json_rpc.JsonRpcMethod -import com.walletconnect.utils.addDeserializerEntry -import com.walletconnect.utils.addSerializerEntry -import org.koin.dsl.module - -@JvmSynthetic -internal fun jsonRpcModule() = module { - addSerializerEntry(ChatRpc.ChatMessage::class) - addSerializerEntry(ChatRpc.ChatInvite::class) - addSerializerEntry(ChatRpc.ChatLeave::class) - addSerializerEntry(ChatRpc.ChatPing::class) - - addDeserializerEntry(JsonRpcMethod.WC_CHAT_MESSAGE, ChatRpc.ChatMessage::class) - addDeserializerEntry(JsonRpcMethod.WC_CHAT_INVITE, ChatRpc.ChatInvite::class) - addDeserializerEntry(JsonRpcMethod.WC_CHAT_LEAVE, ChatRpc.ChatLeave::class) - addDeserializerEntry(JsonRpcMethod.WC_CHAT_PING, ChatRpc.ChatPing::class) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/StorageModule.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/StorageModule.kt deleted file mode 100644 index 1ac141617..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/di/StorageModule.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.walletconnect.chat.di - -import app.cash.sqldelight.ColumnAdapter -import app.cash.sqldelight.EnumColumnAdapter -import com.walletconnect.android.di.sdkBaseStorageModule -import com.walletconnect.android.internal.common.di.deleteDatabase -import com.walletconnect.chat.ChatDatabase -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.common.model.InviteType -import com.walletconnect.chat.storage.* -import com.walletconnect.chat.storage.data.dao.Invites -import org.koin.core.qualifier.named -import org.koin.core.scope.Scope -import org.koin.dsl.module - -@JvmSynthetic -internal fun storageModule(dbName: String) = module { - @Suppress("RemoveExplicitTypeArguments") - fun Scope.createChatDB(): ChatDatabase = ChatDatabase( - driver = get(named(dbName)), - InvitesAdapter = Invites.Adapter( - statusAdapter = get>(named(ChatDITags.COLUMN_ADAPTER_INVITE_STATUS)), - typeAdapter = get>(named(ChatDITags.COLUMN_ADAPTER_INVITE_TYPE)), - ) - ) - - includes(sdkBaseStorageModule(ChatDatabase.Schema, dbName)) - - single { - try { - createChatDB().also { - it.contactsQueries.doesContactNotExists("").executeAsOneOrNull() - } - } catch (e: Exception) { - deleteDatabase(dbName) - createChatDB() - } - } - - single>(named(ChatDITags.COLUMN_ADAPTER_INVITE_STATUS)) { EnumColumnAdapter() } - single>(named(ChatDITags.COLUMN_ADAPTER_INVITE_TYPE)) { EnumColumnAdapter() } - - single { get().contactsQueries } - single { get().threadsQueries } - single { get().invitesQueries } - single { get().messagesQueries } - single { get().accountsQueries } - - single { ContactStorageRepository(get()) } - single { ThreadsStorageRepository(get()) } - single { InvitesStorageRepository(get()) } - single { MessageStorageRepository(get()) } - single { AccountsStorageRepository(get()) } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatEngine.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatEngine.kt deleted file mode 100644 index dd770cb99..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatEngine.kt +++ /dev/null @@ -1,166 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.engine.domain - -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.engine.use_case.SubscribeToChatTopicsUseCase -import com.walletconnect.chat.engine.use_case.calls.AcceptInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.AcceptInviteUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GetMessagesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetMessagesUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GetReceivedInvitesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetReceivedInvitesUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GetSentInvitesUseCase -import com.walletconnect.chat.engine.use_case.calls.GetSentInvitesUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GetThreadsUseCase -import com.walletconnect.chat.engine.use_case.calls.GetThreadsUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GoPrivateUseCase -import com.walletconnect.chat.engine.use_case.calls.GoPrivateUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.GoPublicUseCase -import com.walletconnect.chat.engine.use_case.calls.GoPublicUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.LeaveThreadUseCase -import com.walletconnect.chat.engine.use_case.calls.LeaveThreadUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.RegisterIdentityUseCase -import com.walletconnect.chat.engine.use_case.calls.RegisterIdentityUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.RejectInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.RejectInviteUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.ResolveAccountUseCase -import com.walletconnect.chat.engine.use_case.calls.ResolveAccountUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.SendInviteUseCase -import com.walletconnect.chat.engine.use_case.calls.SendInviteUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.SendMessageUseCase -import com.walletconnect.chat.engine.use_case.calls.SendMessageUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.SendPingUseCase -import com.walletconnect.chat.engine.use_case.calls.SendPingUseCaseInterface -import com.walletconnect.chat.engine.use_case.calls.UnregisterIdentityUseCase -import com.walletconnect.chat.engine.use_case.calls.UnregisterIdentityUseCaseInterface -import com.walletconnect.chat.engine.use_case.requests.OnInviteRequestUseCase -import com.walletconnect.chat.engine.use_case.requests.OnLeaveRequestUseCase -import com.walletconnect.chat.engine.use_case.requests.OnMessageRequestUseCase -import com.walletconnect.chat.engine.use_case.responses.OnInviteResponseUseCase -import com.walletconnect.chat.engine.use_case.responses.OnLeaveResponseUseCase -import com.walletconnect.chat.engine.use_case.responses.OnMessageResponseUseCase -import com.walletconnect.chat.json_rpc.JsonRpcMethod -import com.walletconnect.foundation.common.model.Ttl -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -internal class ChatEngine( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val pairingHandler: PairingControllerInterface, - private val subscribeToChatTopicsUseCase: SubscribeToChatTopicsUseCase, - private val acceptInviteUseCase: AcceptInviteUseCase, - private val rejectInviteUseCase: RejectInviteUseCase, - private val goPublicUseCase: GoPublicUseCase, - private val goPrivateUseCase: GoPrivateUseCase, - private val registerIdentityUseCase: RegisterIdentityUseCase, - private val onInviteRequestUseCase: OnInviteRequestUseCase, - private val onMessageRequestUseCase: OnMessageRequestUseCase, - private val onLeaveRequestUseCase: OnLeaveRequestUseCase, - private val onInviteResponseUseCase: OnInviteResponseUseCase, - private val onMessageResponseUseCase: OnMessageResponseUseCase, - private val onLeaveResponseUseCase: OnLeaveResponseUseCase, - private val sendInviteUseCase: SendInviteUseCase, - private val sendMessageUseCase: SendMessageUseCase, - private val unregisterIdentityUseCase: UnregisterIdentityUseCase, - private val resolveAccountUseCase: ResolveAccountUseCase, - private val leaveThreadUseCase: LeaveThreadUseCase, - private val sendPingUseCase: SendPingUseCase, - private val getThreadsUseCase: GetThreadsUseCase, - private val getMessagesUseCase: GetMessagesUseCase, - private val getSentInvitesUseCase: GetSentInvitesUseCase, - private val getReceivedInvitesUseCase: GetReceivedInvitesUseCase, -) : AcceptInviteUseCaseInterface by acceptInviteUseCase, - RejectInviteUseCaseInterface by rejectInviteUseCase, - RegisterIdentityUseCaseInterface by registerIdentityUseCase, - GoPublicUseCaseInterface by goPublicUseCase, - GoPrivateUseCaseInterface by goPrivateUseCase, - GetThreadsUseCaseInterface by getThreadsUseCase, - GetMessagesUseCaseInterface by getMessagesUseCase, - GetSentInvitesUseCaseInterface by getSentInvitesUseCase, - GetReceivedInvitesUseCaseInterface by getReceivedInvitesUseCase, - UnregisterIdentityUseCaseInterface by unregisterIdentityUseCase, - ResolveAccountUseCaseInterface by resolveAccountUseCase, - LeaveThreadUseCaseInterface by leaveThreadUseCase, - SendPingUseCaseInterface by sendPingUseCase, - SendMessageUseCaseInterface by sendMessageUseCase, - SendInviteUseCaseInterface by sendInviteUseCase { - - private var jsonRpcRequestsJob: Job? = null - private var jsonRpcResponsesJob: Job? = null - private var internalErrorsJob: Job? = null - private var chatEventsJob: Job? = null - - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - // Remove after pairing refactor - init { - pairingHandler.register( - JsonRpcMethod.WC_CHAT_INVITE, - JsonRpcMethod.WC_CHAT_MESSAGE, - JsonRpcMethod.WC_CHAT_LEAVE, - JsonRpcMethod.WC_CHAT_PING - ) - } - - fun setup() { - jsonRpcInteractor.wssConnectionState - .filterIsInstance() - .onEach { - coroutineScope { launch(Dispatchers.IO) { subscribeToChatTopicsUseCase() } } - - if (jsonRpcRequestsJob == null) jsonRpcRequestsJob = collectJsonRpcRequests() - if (jsonRpcResponsesJob == null) jsonRpcResponsesJob = collectPeerResponses() - if (internalErrorsJob == null) internalErrorsJob = collectInternalErrors() - if (chatEventsJob == null) chatEventsJob = collectChatEvents() - }.launchIn(scope) - } - - private fun collectJsonRpcRequests(): Job = jsonRpcInteractor.clientSyncJsonRpc - .filter { request -> request.params is ChatParams } - .onEach { request -> - when (val params = request.params) { - is ChatParams.InviteParams -> onInviteRequestUseCase(request, params) - is ChatParams.MessageParams -> onMessageRequestUseCase(request, params) - is ChatParams.LeaveParams -> onLeaveRequestUseCase(request) - is ChatParams.PingParams -> jsonRpcInteractor.respondWithSuccess(request, IrnParams(Tags.SESSION_PING_RESPONSE, Ttl(thirtySeconds))) - } - }.launchIn(scope) - - private fun collectPeerResponses(): Job = jsonRpcInteractor.peerResponse - .onEach { response -> - when (response.params) { - is ChatParams.InviteParams, is ChatNotifyResponseAuthParams.ResponseAuth -> onInviteResponseUseCase(response) - is ChatParams.MessageParams -> onMessageResponseUseCase(response) - is ChatParams.LeaveParams -> onLeaveResponseUseCase(response) - } - }.launchIn(scope) - - private fun collectInternalErrors(): Job = merge(jsonRpcInteractor.internalErrors, pairingHandler.findWrongMethodsFlow, subscribeToChatTopicsUseCase.errors) - .onEach { exception -> _events.emit(exception) } - .launchIn(scope) - - private fun collectChatEvents(): Job = merge(onInviteRequestUseCase.events, onMessageRequestUseCase.events, onLeaveRequestUseCase.events, onInviteResponseUseCase.events) - .onEach { event -> _events.emit(event) } - .launchIn(scope) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatValidator.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatValidator.kt deleted file mode 100644 index 59adf1b15..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/domain/ChatValidator.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.chat.engine.domain - - -internal object ChatValidator { - fun isInviteMessageValid(value : String) : Boolean = value.length <= MAX_LENGTH_INVITE_MESSAGE - fun isChatMessageValid(value : String) : Boolean = value.length <= MAX_LENGTH_CHAT_MESSAGE - fun isMediaDataValid(value : String?) : Boolean = (value?.length ?: 0) <= MAX_LENGTH_MEDIA_DATA - - internal const val MAX_LENGTH_INVITE_MESSAGE = 200 - internal const val MAX_LENGTH_CHAT_MESSAGE = 1000 - internal const val MAX_LENGTH_MEDIA_DATA = 500 -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/SubscribeToChatTopicsUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/SubscribeToChatTopicsUseCase.kt deleted file mode 100644 index 0ae016c72..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/SubscribeToChatTopicsUseCase.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.walletconnect.chat.engine.use_case - -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.chat.storage.AccountsStorageRepository -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal class SubscribeToChatTopicsUseCase( - private val logger: Logger, - private val invitesRepository: InvitesStorageRepository, - private val accountsRepository: AccountsStorageRepository, - private val threadsRepository: ThreadsStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) { - private val _errors: MutableSharedFlow = MutableSharedFlow() - val errors: SharedFlow = _errors.asSharedFlow() - - suspend operator fun invoke() { - trySubscribeToInviteTopics() - trySubscribeToPendingAcceptTopics() - trySubscribeToThreadTopics() - } - - private suspend fun trySubscribeToInviteTopics() = accountsRepository.getAllInviteTopics() - .trySubscribeToTopics("invite") { error -> scope.launch { _errors.emit(SDKError(error)) } } - - private suspend fun trySubscribeToThreadTopics() = threadsRepository.getAllThreads() - .map { it.topic } - .trySubscribeToTopics("thread messages") { error -> scope.launch { _errors.emit(SDKError(error)) } } - - private suspend fun trySubscribeToPendingAcceptTopics() = invitesRepository.getAllPendingSentInvites() - .map { it.acceptTopic } - .trySubscribeToTopics(topicDescription = "invite response") { error -> scope.launch { _errors.emit(SDKError(error)) } } - - private fun List.trySubscribeToTopics(topicDescription: String, onError: (Throwable) -> Unit) = runCatching { - jsonRpcInteractor.batchSubscribe(this.map { it.value }, onFailure = { error -> onError(error) }, onSuccess = { topics -> logger.log("Listening for $topicDescription on: $topics") }) - }.onFailure { error -> onError(error) } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/AcceptInviteUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/AcceptInviteUseCase.kt deleted file mode 100644 index 2eb603f72..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/AcceptInviteUseCase.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.getInviteTag -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.exceptions.InviteWasAlreadyRespondedTo -import com.walletconnect.chat.common.exceptions.MissingInviteRequestException -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.json_rpc.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.chat.jwt.use_case.EncodeInviteApprovalDidJwtPayloadUseCase -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.launch - - -internal class AcceptInviteUseCase( - private val keyserverUrl: String, - private val getPendingJsonRpcHistoryEntryByIdUseCase: GetPendingJsonRpcHistoryEntryByIdUseCase, - private val logger: Logger, - private val invitesRepository: InvitesStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val identitiesInteractor: IdentitiesInteractor, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val threadsRepository: ThreadsStorageRepository, -) : AcceptInviteUseCaseInterface { - - override fun accept(inviteId: Long, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) { - scope.launch { - try { - val jsonRpcHistoryEntry = getPendingJsonRpcHistoryEntryByIdUseCase(inviteId) - - if (jsonRpcHistoryEntry == null) { - logger.error(MissingInviteRequestException) - return@launch onError(MissingInviteRequestException) - } - - val invite = invitesRepository.getReceivedInviteByInviteId(inviteId) - if (invite.status == InviteStatus.APPROVED || invite.status == InviteStatus.REJECTED) { - logger.error(InviteWasAlreadyRespondedTo) - return@launch onError(InviteWasAlreadyRespondedTo) - } - - val inviterPublicKey = invite.inviterPublicKey - val inviteeAccountId = invite.inviteeAccount - val inviterAccountId = invite.inviterAccount - - val inviteePublicKey = keyManagementRepository.getPublicKey(inviteeAccountId.getInviteTag()) - val symmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(inviteePublicKey, inviterPublicKey) - val acceptTopic = keyManagementRepository.getTopicFromKey(symmetricKey) - keyManagementRepository.setKey(symmetricKey, acceptTopic.value) - - val publicKey = keyManagementRepository.generateAndStoreX25519KeyPair() - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(inviteeAccountId) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeInviteApprovalDidJwtPayloadUseCase(publicKey, inviterAccountId), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ).getOrElse { error -> return@launch onError(error) } - - val acceptanceParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = didJwt.value) - val responseParams = JsonRpcResponse.JsonRpcResult(jsonRpcHistoryEntry.id, result = acceptanceParams) - val irnParams = IrnParams(Tags.CHAT_INVITE_RESPONSE, Ttl(monthInSeconds)) - jsonRpcInteractor.publishJsonRpcResponse(acceptTopic, irnParams, responseParams, {}, { error -> return@publishJsonRpcResponse onError(error) }) - - val threadSymmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(publicKey, inviterPublicKey) - val threadTopic = keyManagementRepository.getTopicFromKey(threadSymmetricKey) - keyManagementRepository.setKey(threadSymmetricKey, threadTopic.value) - - threadsRepository.insertThread(threadTopic.value, selfAccount = inviteeAccountId.value, peerAccount = inviterAccountId.value) - invitesRepository.updateStatusByInviteId(inviteId, InviteStatus.APPROVED) - - jsonRpcInteractor.subscribe(threadTopic) { error -> return@subscribe onError(error) } - onSuccess(threadTopic.value) - - } catch (error: Exception) { - onError(error) - } - } - } -} - -internal interface AcceptInviteUseCaseInterface { - fun accept(inviteId: Long, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetMessagesUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetMessagesUseCase.kt deleted file mode 100644 index ad470adca..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetMessagesUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.chat.common.model.Message -import com.walletconnect.chat.storage.MessageStorageRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.runBlocking - - -internal class GetMessagesUseCase( - private val messageRepository: MessageStorageRepository, -) : GetMessagesUseCaseInterface { - - override fun getMessages(topic: String): List { - val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - return runBlocking(scope.coroutineContext) { - messageRepository.getMessageByTopic(topic) - } - } -} - -internal interface GetMessagesUseCaseInterface { - fun getMessages(topic: String): List -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetReceivedInvitesUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetReceivedInvitesUseCase.kt deleted file mode 100644 index 7319079a0..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetReceivedInvitesUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.storage.InvitesStorageRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.runBlocking - - -internal class GetReceivedInvitesUseCase( - private val invitesRepository: InvitesStorageRepository, -) : GetReceivedInvitesUseCaseInterface { - - override fun getReceivedInvites(inviteeAccountId: String): Map { - val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - return runBlocking(scope.coroutineContext) { - invitesRepository.getReceivedInvitesForInviteeAccount(inviteeAccountId).associateBy { invite -> invite.id } - } - } -} - -internal interface GetReceivedInvitesUseCaseInterface { - fun getReceivedInvites(inviteeAccountId: String): Map -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetSentInvitesUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetSentInvitesUseCase.kt deleted file mode 100644 index daa7be536..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetSentInvitesUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.storage.InvitesStorageRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.runBlocking - - -internal class GetSentInvitesUseCase( - private val invitesRepository: InvitesStorageRepository, -) : GetSentInvitesUseCaseInterface { - - override fun getSentInvites(inviterAccountId: String): Map { - val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - return runBlocking(scope.coroutineContext) { - invitesRepository.getSentInvitesForInviterAccount(inviterAccountId).associateBy { invite -> invite.id } - } - } -} - -internal interface GetSentInvitesUseCaseInterface { - fun getSentInvites(inviterAccountId: String): Map -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetThreadsUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetThreadsUseCase.kt deleted file mode 100644 index 6692c1c03..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GetThreadsUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.chat.common.model.Thread -import com.walletconnect.chat.storage.ThreadsStorageRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.runBlocking - - -internal class GetThreadsUseCase( - private val threadsRepository: ThreadsStorageRepository, -) : GetThreadsUseCaseInterface { - - override fun getThreads(accountId: String): Map { - val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - return runBlocking(scope.coroutineContext) { - threadsRepository.getThreadsForSelfAccount(accountId).associateBy { thread -> thread.topic.value } - } - } - -} - -internal interface GetThreadsUseCaseInterface { - fun getThreads(accountId: String): Map -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPrivateUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPrivateUseCase.kt deleted file mode 100644 index 675b8d2dc..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPrivateUseCase.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.getInviteTag -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.android.keyserver.domain.use_case.UnregisterInviteUseCase -import com.walletconnect.chat.common.exceptions.InvalidAccountIdException -import com.walletconnect.chat.common.exceptions.InviteKeyNotFound -import com.walletconnect.chat.jwt.use_case.EncodeUnregisterInviteKeyDidJwtPayloadUseCase -import com.walletconnect.chat.storage.AccountsStorageRepository -import com.walletconnect.foundation.util.jwt.encodeX25519DidKey -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class GoPrivateUseCase( - private val keyserverUrl: String, - private val identitiesInteractor: IdentitiesInteractor, - private val accountsRepository: AccountsStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val unregisterInviteUseCase: UnregisterInviteUseCase, -) : GoPrivateUseCaseInterface { - - override fun goPrivate(accountId: AccountId, onSuccess: () -> Unit, onError: (Throwable) -> Unit) { - if (accountId.isValid()) { - try { - val invitePublicKey = keyManagementRepository.getPublicKey(accountId.getInviteTag()) - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(accountId) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeUnregisterInviteKeyDidJwtPayloadUseCase(encodeX25519DidKey(invitePublicKey.keyAsBytes), accountId), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ).getOrElse() { error -> return@goPrivate onError(error) } - - scope.launch { - supervisorScope { - unregisterInviteUseCase(didJwt.value).fold( - onSuccess = { - accountsRepository.removeAccountPublicInviteKey(accountId) - keyManagementRepository.removeKeys(accountId.getInviteTag()) - val inviteTopic = keyManagementRepository.getTopicFromKey(invitePublicKey) - keyManagementRepository.removeKeys(inviteTopic.getParticipantTag()) - jsonRpcInteractor.unsubscribe(inviteTopic) - onSuccess() - }, - onFailure = { error -> onError(error) } - ) - } - } - } catch (e: MissingKeyException) { - onError(InviteKeyNotFound("Unable to find stored invite key for $accountId")) - } - - } else { - onError(InvalidAccountIdException("AccountId is not CAIP-10 complaint")) - } - } -} - -internal interface GoPrivateUseCaseInterface { - fun goPrivate(accountId: AccountId, onSuccess: () -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPublicUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPublicUseCase.kt deleted file mode 100644 index 3ac3ba421..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/GoPublicUseCase.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.getInviteTag -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.android.keyserver.domain.use_case.RegisterInviteUseCase -import com.walletconnect.chat.common.exceptions.InvalidAccountIdException -import com.walletconnect.chat.jwt.use_case.EncodeRegisterInviteKeyDidJwtPayloadUseCase -import com.walletconnect.chat.storage.AccountsStorageRepository -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeX25519DidKey -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class GoPublicUseCase( - private val keyserverUrl: String, - private val identitiesInteractor: IdentitiesInteractor, - private val accountsRepository: AccountsStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val registerInviteUseCase: RegisterInviteUseCase, -) : GoPublicUseCaseInterface { - - override fun goPublic(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) { - fun onSuccess(invitePublicKey: PublicKey, invitePrivateKey: PrivateKey) { - scope.launch { - supervisorScope { - val inviteTopic = keyManagementRepository.getTopicFromKey(invitePublicKey) - keyManagementRepository.setKey(invitePublicKey, accountId.getInviteTag()) - keyManagementRepository.setKey(invitePublicKey, inviteTopic.getParticipantTag()) - accountsRepository.setAccountPublicInviteKey(accountId, invitePublicKey, inviteTopic) - jsonRpcInteractor.subscribe(inviteTopic) - - onSuccess(invitePublicKey.keyAsHex) - } - } - } - - if (accountId.isValid()) { - try { - val storedPublicKey = keyManagementRepository.getPublicKey(accountId.getInviteTag()) - onSuccess(storedPublicKey.keyAsHex) - } catch (e: MissingKeyException) { - val (invitePublicKey, invitePrivateKey) = keyManagementRepository.generateAndStoreX25519KeyPair().let { invitePublicKey -> keyManagementRepository.getKeyPair(invitePublicKey) } - - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(accountId) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeRegisterInviteKeyDidJwtPayloadUseCase(encodeX25519DidKey(invitePublicKey.keyAsBytes), accountId), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ).getOrElse() { error -> return@goPublic onError(error) } - - scope.launch { - supervisorScope { - registerInviteUseCase(didJwt.value).fold( - onSuccess = { onSuccess(invitePublicKey, invitePrivateKey) }, - onFailure = { error -> onError(error) } - ) - } - } - } - } else { - onError(InvalidAccountIdException("AccountId is not CAIP-10 complaint")) - } - } -} - -internal interface GoPublicUseCaseInterface { - fun goPublic(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/LeaveThreadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/LeaveThreadUseCase.kt deleted file mode 100644 index e26f0199e..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/LeaveThreadUseCase.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.launch - - -internal class LeaveThreadUseCase( - private val logger: Logger, - private val threadsRepository: ThreadsStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) : LeaveThreadUseCaseInterface { - override fun leave(topic: String, onError: (Throwable) -> Unit) { - val payload = ChatRpc.ChatLeave(params = ChatParams.LeaveParams()) - val irnParams = IrnParams(Tags.CHAT_LEAVE, Ttl(monthInSeconds), true) - - jsonRpcInteractor.publishJsonRpcRequest( - Topic(topic), irnParams, payload, EnvelopeType.ZERO, - onSuccess = { - // Not sure if we want to remove thread and messages if someone leaves convo. - // Maybe just forgetting thread symkey is better solution? - scope.launch { - threadsRepository.deleteThreadByTopic(topic) - jsonRpcInteractor.unsubscribe(Topic(topic)) { error -> onError(error) } - } - }, - onFailure = { throwable -> onError(throwable).also { logger.error(throwable) } }) - } -} - -internal interface LeaveThreadUseCaseInterface { - fun leave(topic: String, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RegisterIdentityUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RegisterIdentityUseCase.kt deleted file mode 100644 index 476007803..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RegisterIdentityUseCase.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.storage.AccountsStorageRepository -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class RegisterIdentityUseCase( - private val keyserverUrl: String, - private val identitiesInteractor: IdentitiesInteractor, - private val accountsRepository: AccountsStorageRepository, - private val goPublicUseCase: GoPublicUseCase, -) : RegisterIdentityUseCaseInterface { - - override fun register(accountId: AccountId, onSign: (String) -> Cacao.Signature?, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit, private: Boolean) { - scope.launch { - supervisorScope { -// identitiesInteractor.registerIdentity(accountId, keyserverUrl, onSign).fold( -// onFailure = { error -> onError(error) }, -// onSuccess = { identityPublicKey -> -// accountsRepository.upsertAccount(Account(accountId, identityPublicKey, null, null)) -// val didKey = encodeEd25519DidKey(identityPublicKey.keyAsBytes) -// setupSyncInChatUseCase(accountId, onSign, onError = onError, onSuccess = { -// if (!private) { -// goPublicUseCase.goPublic(accountId, onSuccess = { onSuccess(didKey) }, onError = { error -> onError(error) }) -// } else { -// onSuccess(didKey) -// } -// }) -// } -// ) - } - } - } -} - -internal interface RegisterIdentityUseCaseInterface { - fun register(accountId: AccountId, onSign: (String) -> Cacao.Signature?, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit, private: Boolean) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RejectInviteUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RejectInviteUseCase.kt deleted file mode 100644 index 37f926793..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/RejectInviteUseCase.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.MissingKeyException -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.internal.utils.getInviteTag -import com.walletconnect.chat.common.exceptions.InviteWasAlreadyRespondedTo -import com.walletconnect.chat.common.exceptions.MissingInviteRequestException -import com.walletconnect.chat.common.exceptions.PeerError -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.json_rpc.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.launch - - -internal class RejectInviteUseCase( - private val getPendingJsonRpcHistoryEntryByIdUseCase: GetPendingJsonRpcHistoryEntryByIdUseCase, - private val logger: Logger, - private val invitesRepository: InvitesStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) : RejectInviteUseCaseInterface { - override fun reject(inviteId: Long, onSuccess: () -> Unit, onError: (Throwable) -> Unit) { - scope.launch { - try { - val jsonRpcHistoryEntry = getPendingJsonRpcHistoryEntryByIdUseCase(inviteId) ?: return@launch onError(MissingInviteRequestException).also { logger.error(MissingInviteRequestException) } - - val invite = invitesRepository.getReceivedInviteByInviteId(inviteId) - if (invite.status == InviteStatus.APPROVED || invite.status == InviteStatus.REJECTED) { - return@launch onError(InviteWasAlreadyRespondedTo).also { logger.error(InviteWasAlreadyRespondedTo) } - } - - val inviterPublicKey = invite.inviterPublicKey - val inviteeAccountId = invite.inviteeAccount - - val inviteePublicKey = keyManagementRepository.getPublicKey(inviteeAccountId.getInviteTag()) - val symmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(inviteePublicKey, inviterPublicKey) - val rejectTopic = keyManagementRepository.getTopicFromKey(symmetricKey) - keyManagementRepository.setKey(symmetricKey, rejectTopic.value) - - val irnParams = IrnParams(Tags.CHAT_INVITE_RESPONSE, Ttl(monthInSeconds)) - val peerError = PeerError.UserRejectedInvitation("Invitation rejected by a user") - val responseParams = JsonRpcResponse.JsonRpcError(jsonRpcHistoryEntry.id, error = JsonRpcResponse.Error(peerError.code, peerError.message)) - jsonRpcInteractor.publishJsonRpcResponse(rejectTopic, irnParams, responseParams, {}, { error -> return@publishJsonRpcResponse onError(error) }) - invitesRepository.updateStatusByInviteId(inviteId, InviteStatus.REJECTED) - onSuccess() - } catch (e: MissingKeyException) { - return@launch onError(e) - } - } - } -} - -internal interface RejectInviteUseCaseInterface { - fun reject(inviteId: Long, onSuccess: () -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/ResolveAccountUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/ResolveAccountUseCase.kt deleted file mode 100644 index 308a7c800..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/ResolveAccountUseCase.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.keyserver.domain.use_case.ResolveInviteUseCase -import com.walletconnect.chat.common.exceptions.InvalidAccountIdException -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class ResolveAccountUseCase( - private val resolveInviteUseCase: ResolveInviteUseCase, -) : ResolveAccountUseCaseInterface { - - override fun resolveAccount(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) { - if (accountId.isValid()) { - scope.launch { - supervisorScope { - resolveInviteUseCase(accountId).fold( - onSuccess = { response -> onSuccess(response.inviteKey) }, - onFailure = { error -> onError(error) } - ) - } - } - } else { - onError(InvalidAccountIdException("AccountId is not CAIP-10 complaint")) - } - } -} - -internal interface ResolveAccountUseCaseInterface { - fun resolveAccount(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendInviteUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendInviteUseCase.kt deleted file mode 100644 index 87db44197..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendInviteUseCase.kt +++ /dev/null @@ -1,134 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.exceptions.AccountsAlreadyHaveInviteException -import com.walletconnect.chat.common.exceptions.AccountsAlreadyHaveThreadException -import com.walletconnect.chat.common.exceptions.InviteMessageTooLongException -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.chat.common.model.Contact -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.common.model.SendInvite -import com.walletconnect.chat.engine.domain.ChatValidator -import com.walletconnect.chat.jwt.use_case.EncodeInviteProposalDidJwtPayloadUseCase -import com.walletconnect.chat.storage.ContactStorageRepository -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeX25519DidKey -import com.walletconnect.util.generateId -import com.walletconnect.utils.extractTimestamp -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext - - -internal class SendInviteUseCase( - private val keyserverUrl: String, - private val logger: Logger, - private val invitesRepository: InvitesStorageRepository, - private val threadsRepository: ThreadsStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val identitiesInteractor: IdentitiesInteractor, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val contactRepository: ContactStorageRepository, -) : SendInviteUseCaseInterface { - - override suspend fun invite(invite: SendInvite, onSuccess: (Long) -> Unit, onError: (Throwable) -> Unit) { - if (!ChatValidator.isInviteMessageValid(invite.message.value)) { - return onError(InviteMessageTooLongException()) - } - - val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - if (withContext(scope.coroutineContext) { invitesRepository.checkIfAccountsHaveExistingInvite(invite.inviterAccount.value, invite.inviteeAccount.value) }) { - return onError(AccountsAlreadyHaveInviteException) - } - - if (runBlocking(scope.coroutineContext) { threadsRepository.checkIfSelfAccountHaveThreadWithPeerAccount(invite.inviterAccount.value, invite.inviteeAccount.value) }) { - return onError(AccountsAlreadyHaveThreadException) - } - - if (runBlocking(scope.coroutineContext) { threadsRepository.checkIfSelfAccountHaveThreadWithPeerAccount(invite.inviteeAccount.value, invite.inviterAccount.value) }) { - return onError(AccountsAlreadyHaveThreadException) - } - - val decodedInviteePublicKey = decodeX25519DidKey(invite.inviteePublicKey) - - runCatching { withContext(scope.coroutineContext) { setContact(invite.inviteeAccount, decodedInviteePublicKey) } }.getOrElse { error -> return onError(error) } - - val inviterPublicKey = runCatching { keyManagementRepository.generateAndStoreX25519KeyPair() }.getOrElse { error -> return onError(error) } - val inviterPrivateKey = keyManagementRepository.getKeyPair(inviterPublicKey).second - - try { - val symmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(inviterPublicKey, decodedInviteePublicKey) - val inviteTopic = keyManagementRepository.getTopicFromKey(decodedInviteePublicKey) - keyManagementRepository.setKeyAgreement(inviteTopic, inviterPublicKey, decodedInviteePublicKey) - - val participants = Participants(senderPublicKey = inviterPublicKey, receiverPublicKey = decodedInviteePublicKey) - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(invite.inviterAccount) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeInviteProposalDidJwtPayloadUseCase(inviterPublicKey, invite.inviteeAccount, invite.message.value), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ).getOrElse() { error -> return@invite onError(error) } - - - val inviteParams = ChatParams.InviteParams(inviteAuth = didJwt.value) - val inviteId = generateId() - val payload = ChatRpc.ChatInvite(id = inviteId, params = inviteParams) - val acceptTopic = keyManagementRepository.getTopicFromKey(symmetricKey) - - keyManagementRepository.setKey(symmetricKey, acceptTopic.value) - jsonRpcInteractor.subscribe(acceptTopic) { error -> return@subscribe onError(error) } - - val irnParams = IrnParams(Tags.CHAT_INVITE, Ttl(monthInSeconds), true) - jsonRpcInteractor.publishJsonRpcRequest(inviteTopic, irnParams, payload, EnvelopeType.ONE, participants, - { - val sentInvite = Invite.Sent( - inviteId, invite.inviterAccount, invite.inviteeAccount, invite.message, inviterPublicKey, - InviteStatus.PENDING, acceptTopic, symmetricKey, inviterPrivateKey, - //todo: use publishedAt from relay https://github.com/WalletConnect/WalletConnectKotlinV2/issues/872 - timestamp = inviteId.extractTimestamp() - ) - scope.launch { invitesRepository.insertInvite(sentInvite) } - onSuccess(inviteId) - }, - { throwable -> - logger.error(throwable) - jsonRpcInteractor.unsubscribe(acceptTopic) - onError(throwable) - } - ) - } catch (error: Exception) { - keyManagementRepository.removeKeys(inviterPublicKey.keyAsHex) - onError(error) - } - } - - private suspend fun setContact(accountId: AccountId, publicInviteKey: PublicKey) { - contactRepository.upsertContact(Contact(accountId, publicInviteKey, accountId.value)) - } - - -} - -internal interface SendInviteUseCaseInterface { - suspend fun invite(invite: SendInvite, onSuccess: (Long) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendMessageUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendMessageUseCase.kt deleted file mode 100644 index 3eadeb2f2..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendMessageUseCase.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.exceptions.ChatMessageTooLongException -import com.walletconnect.chat.common.exceptions.MediaDataTooLongException -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.chat.common.model.Message -import com.walletconnect.chat.common.model.SendMessage -import com.walletconnect.chat.engine.domain.ChatValidator -import com.walletconnect.chat.jwt.use_case.EncodeChatMessageDidJwtPayloadUseCase -import com.walletconnect.chat.storage.MessageStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.util.generateId -import com.walletconnect.utils.extractTimestamp -import kotlinx.coroutines.launch - - -internal class SendMessageUseCase( - private val keyserverUrl: String, - private val logger: Logger, - private val threadsRepository: ThreadsStorageRepository, - private val identitiesInteractor: IdentitiesInteractor, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val messageRepository: MessageStorageRepository, -) : SendMessageUseCaseInterface { - - override fun message(topic: String, message: SendMessage, onSuccess: () -> Unit, onError: (Throwable) -> Unit) { - scope.launch { - if (!ChatValidator.isChatMessageValid(message.message.value)) { - return@launch onError(ChatMessageTooLongException()) - } else if (!ChatValidator.isMediaDataValid(message.media?.data?.value)) { - return@launch onError(MediaDataTooLongException()) - } - - val thread = threadsRepository.getThreadByTopic(topic) - val (authorAccountId, recipientAccountId) = thread.selfAccount to thread.peerAccount - val messageId = generateId() - val messageTimestampInMs = messageId.extractTimestamp() - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(authorAccountId) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeChatMessageDidJwtPayloadUseCase(message.message.value, recipientAccountId, message.media, messageTimestampInMs), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ) - .getOrElse() { error -> return@launch onError(error) } - - val messageParams = ChatParams.MessageParams(messageAuth = didJwt.value) - val payload = ChatRpc.ChatMessage(id = messageId, params = messageParams) - val irnParams = IrnParams(Tags.CHAT_MESSAGE, Ttl(monthInSeconds), true) - - messageRepository.insertMessage(Message(messageId, Topic(topic), message.message, authorAccountId, messageTimestampInMs, message.media)) - jsonRpcInteractor.publishJsonRpcRequest( - Topic(topic), irnParams, payload, - onSuccess = { onSuccess() }, - onFailure = { throwable -> - logger.error(throwable) - scope.launch { messageRepository.deleteMessageByMessageId(messageId) } - onError(throwable) - }) - } - } -} - -internal interface SendMessageUseCaseInterface { - fun message(topic: String, message: SendMessage, onSuccess: () -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendPingUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendPingUseCase.kt deleted file mode 100644 index 0e1a38315..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/SendPingUseCase.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout - - -internal class SendPingUseCase( - private val logger: Logger, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) : SendPingUseCaseInterface { - - override fun ping(topic: String, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) { - val pingPayload = ChatRpc.ChatPing(params = ChatParams.PingParams()) - val irnParams = IrnParams(Tags.CHAT_PING, Ttl(thirtySeconds)) - - jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, pingPayload, - onSuccess = { pingSuccess(pingPayload, onSuccess, topic, onError) }, - onFailure = { error -> onError(error).also { logger.error("Ping sent error: $error") } }) - } - - private fun pingSuccess( - pingPayload: ChatRpc.ChatPing, - onSuccess: (String) -> Unit, - topic: String, - onError: (Throwable) -> Unit, - ) { - logger.log("Ping sent successfully") - scope.launch { - try { - withTimeout(THIRTY_SECONDS_TIMEOUT) { - collectResponse(pingPayload.id) { result -> - cancel() - result.fold( - onSuccess = { - logger.log("Ping peer response success") - onSuccess(topic) - }, - onFailure = { error -> - logger.log("Ping peer response error: $error") - onError(error) - }) - } - } - } catch (e: TimeoutCancellationException) { - onError(e) - } - } - } - - private suspend fun collectResponse(id: Long, onResponse: (Result) -> Unit = {}) { - jsonRpcInteractor.peerResponse - .filter { response -> response.response.id == id } - .collect { response -> - when (val result = response.response) { - is JsonRpcResponse.JsonRpcResult -> onResponse(Result.success(result)) - is JsonRpcResponse.JsonRpcError -> onResponse(Result.failure(Throwable(result.errorMessage))) - } - } - } - - private companion object { - const val THIRTY_SECONDS_TIMEOUT: Long = 30000L - } -} - -internal interface SendPingUseCaseInterface { - fun ping(topic: String, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/UnregisterIdentityUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/UnregisterIdentityUseCase.kt deleted file mode 100644 index ea4c59af7..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/calls/UnregisterIdentityUseCase.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.walletconnect.chat.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.getInviteTag -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.storage.AccountsStorageRepository -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class UnregisterIdentityUseCase( - private val keyserverUrl: String, - private val identitiesInteractor: IdentitiesInteractor, - private val accountsRepository: AccountsStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) : UnregisterIdentityUseCaseInterface { - - override fun unregister(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) { - scope.launch { - supervisorScope { - identitiesInteractor.unregisterIdentity(accountId, keyserverUrl).fold( - onFailure = { error -> onError(error) }, - onSuccess = { identityPublicKey -> - val account = accountsRepository.getAccountByAccountId(accountId) - if (account.publicInviteKey != null && account.inviteTopic != null) { - keyManagementRepository.removeKeys(accountId.getInviteTag()) - keyManagementRepository.removeKeys(account.inviteTopic.getParticipantTag()) - jsonRpcInteractor.unsubscribe(account.inviteTopic) - } - accountsRepository.deleteAccountByAccountId(accountId) - val didKey = encodeEd25519DidKey(identityPublicKey.keyAsBytes) - onSuccess(didKey) - } - ) - } - } - } -} - -internal interface UnregisterIdentityUseCaseInterface { - fun unregister(accountId: AccountId, onSuccess: (String) -> Unit, onError: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnInviteRequestUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnInviteRequestUseCase.kt deleted file mode 100644 index f158d80d9..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnInviteRequestUseCase.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.walletconnect.chat.engine.use_case.requests - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.exceptions.AccountsAlreadyHaveInviteException -import com.walletconnect.chat.common.exceptions.AccountsAlreadyHaveThreadException -import com.walletconnect.chat.common.exceptions.InvalidActClaims -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.common.model.InviteMessage -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.chat.storage.AccountsStorageRepository -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeX25519DidKey -import com.walletconnect.utils.extractTimestamp -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -internal class OnInviteRequestUseCase( - private val logger: Logger, - private val identitiesInteractor: IdentitiesInteractor, - private val accountsRepository: AccountsStorageRepository, - private val invitesRepository: InvitesStorageRepository, - private val threadsRepository: ThreadsStorageRepository, - private val keyManagementRepository: KeyManagementRepository, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcRequest: WCRequest, params: ChatParams.InviteParams) { - val claims = extractVerifiedDidJwtClaims(params.inviteAuth).getOrElse() { error -> return logger.error(error) } - if (claims.action != ChatDidJwtClaims.InviteProposal.ACT) return logger.error(InvalidActClaims(ChatDidJwtClaims.InviteProposal.ACT)) - - val inviterAccountId = identitiesInteractor.resolveIdentityDidKey(claims.issuer).getOrElse() { error -> return logger.error(error) } - - runCatching { accountsRepository.getAccountByInviteTopic(wcRequest.topic) }.fold(onSuccess = { inviteeAccount -> - if (invitesRepository.checkIfAccountsHaveExistingInvite(inviterAccountId.value, inviteeAccount.accountId.value)) { - return logger.error(AccountsAlreadyHaveInviteException) - } - - if (threadsRepository.checkIfSelfAccountHaveThreadWithPeerAccount(inviteeAccount.accountId.value, inviterAccountId.value)) { - return logger.error(AccountsAlreadyHaveThreadException) - } - - val inviteePublicKey = inviteeAccount.publicInviteKey ?: throw Throwable("Missing publicInviteKey") - val inviterPublicKey = decodeX25519DidKey(claims.inviterPublicKey) - val symmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(inviteePublicKey, inviterPublicKey) - val acceptTopic = keyManagementRepository.getTopicFromKey(symmetricKey) - val invite = Invite.Received( - wcRequest.id, inviterAccountId, inviteeAccount.accountId, InviteMessage(claims.subject), inviterPublicKey, InviteStatus.PENDING, acceptTopic, symmetricKey, inviterPrivateKey = null, - //todo: use publishedAt from relay https://github.com/WalletConnect/WalletConnectKotlinV2/issues/872 - timestamp = wcRequest.id.extractTimestamp() - ) - - invitesRepository.insertInvite(invite) - _events.emit(Events.OnInvite(invite)) - }, onFailure = { error -> logger.error(error) }) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnLeaveRequestUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnLeaveRequestUseCase.kt deleted file mode 100644 index 61583a3e6..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnLeaveRequestUseCase.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.chat.engine.use_case.requests - -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.common.model.Ttl -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal class OnLeaveRequestUseCase( - private val threadsRepository: ThreadsStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - operator fun invoke(wcRequest: WCRequest) { - // Not sure if we want to remove thread and messages if someone leaves convo. - // Maybe just forgetting thread symkey is better solution? - scope.launch { - threadsRepository.deleteThreadByTopic(wcRequest.topic.value) - } - - scope.launch { - _events.emit(Events.OnLeft(wcRequest.topic.value)) - jsonRpcInteractor.respondWithSuccess(wcRequest, IrnParams(Tags.CHAT_LEAVE_RESPONSE, Ttl(monthInSeconds))) - } - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnMessageRequestUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnMessageRequestUseCase.kt deleted file mode 100644 index 5a974b458..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/requests/OnMessageRequestUseCase.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.walletconnect.chat.engine.use_case.requests - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.params.CoreChatParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.exceptions.InvalidActClaims -import com.walletconnect.chat.common.json_rpc.ChatParams -import com.walletconnect.chat.common.model.ChatMessage -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.common.model.Message -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.chat.jwt.use_case.EncodeChatReceiptDidJwtPayloadUseCase -import com.walletconnect.chat.storage.MessageStorageRepository -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal class OnMessageRequestUseCase( - private val logger: Logger, - private val identitiesInteractor: IdentitiesInteractor, - private val messageRepository: MessageStorageRepository, - private val keyserverUrl: String, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcRequest: WCRequest, params: ChatParams.MessageParams) { - val claims = extractVerifiedDidJwtClaims(params.messageAuth).getOrElse() { error -> return@invoke logger.error(error) } - if (claims.action != ChatDidJwtClaims.ChatMessage.ACT) return logger.error(InvalidActClaims(ChatDidJwtClaims.ChatMessage.ACT)) - - scope.launch { - val authorAccountId = identitiesInteractor.resolveIdentityDidKey(claims.issuer) - .getOrElse() { error -> return@launch logger.error(error) } - - val message = Message(wcRequest.id, wcRequest.topic, ChatMessage(claims.subject), authorAccountId, claims.issuedAt, claims.media) - messageRepository.insertMessage(message) - _events.emit(Events.OnMessage(message)) - - // With sync integration we need to check if I'm the author by checking if I have identity keys for the account - // If they exists it means I authored the message from other client - // If they don't exists it means someone sent me the message - val recipientAccountId = AccountId(decodeDidPkh(claims.audience)) - - if (shouldClientRespondToRequest(authorAccountId, recipientAccountId)) { - // Currently timestamps are based on claims issuedAt. Which MUST be changed to achieve proper order of messages. - // Should be changed with specs: https://github.com/WalletConnect/walletconnect-docs/pull/473. - // Change: Instead of claims.issuedAt use wcRequest.receivedAt - - val (identityPublicKey, identityPrivateKey) = identitiesInteractor.getIdentityKeyPair(recipientAccountId) - - val didJwt = encodeDidJwt( - identityPrivateKey, - EncodeChatReceiptDidJwtPayloadUseCase(claims.subject, authorAccountId), - EncodeDidJwtPayloadUseCase.Params(identityPublicKey, keyserverUrl) - ).getOrElse() { error -> return@launch logger.error(error) } - - val receiptParams = CoreChatParams.ReceiptParams(receiptAuth = didJwt.value) - val irnParams = IrnParams(Tags.CHAT_MESSAGE_RESPONSE, Ttl(monthInSeconds)) - - jsonRpcInteractor.respondWithParams(wcRequest, receiptParams, irnParams, EnvelopeType.ZERO, onFailure = { error -> logger.error(error) }) - } - } - } - - /** - * Rare case when a device has clients Ax and Bx registered and needs to handle messages between them. - * Context: When I sent a message from A1 client, it was impossible to distinct if I should respond to message with sync, - * as I received message request on A2 and Bx clients - * Client doesn't send the response only when it has author keys, if it has both author and recipient then it does send the response - */ - private fun shouldClientRespondToRequest(authorAccountId: AccountId, recipientAccountId: AccountId): Boolean { - return runCatching { identitiesInteractor.getIdentityKeyPair(authorAccountId) } - .getOrNull()?.let { - // If author account is registered check if recipient is also registered - runCatching { identitiesInteractor.getIdentityKeyPair(recipientAccountId) }.getOrNull()?.let { - true // If both author account and recipient account are registered respond to request - } ?: false // If only author account is register do not respond to request - } ?: true // If author account is not registered respond to request - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnInviteResponseUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnInviteResponseUseCase.kt deleted file mode 100644 index 894ff08a4..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnInviteResponseUseCase.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.walletconnect.chat.engine.use_case.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.chat.common.model.Events -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.storage.InvitesStorageRepository -import com.walletconnect.chat.storage.ThreadsStorageRepository -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch - -internal class OnInviteResponseUseCase( - private val logger: Logger, - private val invitesRepository: InvitesStorageRepository, - private val keyManagementRepository: KeyManagementRepository, - private val identitiesInteractor: IdentitiesInteractor, - private val threadsRepository: ThreadsStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse) { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcError -> onRejected(wcResponse) - is JsonRpcResponse.JsonRpcResult -> onAccepted(response, wcResponse) - } - } - - private suspend fun onRejected(wcResponse: WCResponse) { - scope.launch { - invitesRepository.updateStatusByInviteId(wcResponse.response.id, InviteStatus.REJECTED) - val sentInvite = invitesRepository.getSentInviteByInviteId(wcResponse.response.id) - _events.emit(Events.OnInviteRejected(sentInvite)) - } - } - - private suspend fun onAccepted(response: JsonRpcResponse.JsonRpcResult, wcResponse: WCResponse) { -// -// //TODO this method is called because ChatNotifyResponseAuthParams.ResponseAuth is to generic -// //TODO remove return and handle this being called when it shouldn't -// return -// -// val acceptParams = response.result as ChatNotifyResponseAuthParams.ResponseAuth -// -// // TODO -// // Discuss what state is invite in if not verified -// // invitesRepository.updateStatusByInviteId(wcResponse.response.id, InviteStatus.?????????) -// val claims = extractVerifiedDidJwtClaims(acceptParams.responseAuth).getOrElse() { error -> return@onAccepted logger.error(error) } -// if (claims.action != ChatDidJwtClaims.InviteApproval.ACT) return logger.error(InvalidActClaims(ChatDidJwtClaims.InviteApproval.ACT)) -// -// try { -// val sentInvite = invitesRepository.getSentInviteByInviteId(wcResponse.response.id) -// -// if (sentInvite.status == InviteStatus.APPROVED || sentInvite.status == InviteStatus.REJECTED) return logger.error(InviteResponseWasAlreadyReceived) -// -// val selfPubKey: PublicKey = sentInvite.inviterPublicKey -// val peerPubKey = decodeX25519DidKey(claims.subject) -// val symmetricKey = keyManagementRepository.generateSymmetricKeyFromKeyAgreement(selfPubKey, peerPubKey) -// val threadTopic = keyManagementRepository.getTopicFromKey(symmetricKey) -// keyManagementRepository.setKey(symmetricKey, threadTopic.value) -// -// val inviteeAccountId = identitiesInteractor.resolveIdentityDidKey(claims.issuer).getOrThrow().value -// val inviterAccountId = decodeDidPkh(claims.audience) -// threadsRepository.insertThread(threadTopic.value, selfAccount = inviterAccountId, peerAccount = inviteeAccountId) -// -// jsonRpcInteractor.subscribe(threadTopic) { error -> scope.launch { _events.emit(SDKError(error)) } } -// -// invitesRepository.updateStatusByInviteId(wcResponse.response.id, InviteStatus.APPROVED) -// -// _events.emit(Events.OnInviteAccepted(threadTopic.value, sentInvite)) -// } catch (e: Exception) { -// scope.launch { _events.emit(SDKError(e)) } -// return -// } - } - -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnLeaveResponseUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnLeaveResponseUseCase.kt deleted file mode 100644 index db6eb160b..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnLeaveResponseUseCase.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.walletconnect.chat.engine.use_case.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.foundation.util.Logger - -internal class OnLeaveResponseUseCase( - private val logger: Logger, -) { - operator fun invoke(wcResponse: WCResponse) { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcError -> logger.log("Chat leave error response: $response") - is JsonRpcResponse.JsonRpcResult -> logger.log("Chat leave success response") - } - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnMessageResponseUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnMessageResponseUseCase.kt deleted file mode 100644 index 864a2dac4..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/engine/use_case/responses/OnMessageResponseUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.chat.engine.use_case.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.foundation.util.Logger - -internal class OnMessageResponseUseCase( - private val logger: Logger, -) { - operator fun invoke(wcResponse: WCResponse) { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcError -> logger.log("Message error response: $response") - is JsonRpcResponse.JsonRpcResult -> logger.log("Message success response") - //todo on result validate receipt auth and notify that message was received. Needs discussion and specs (Milestone 2) - } - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/GetPendingJsonRpcHistoryEntryByIdUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/GetPendingJsonRpcHistoryEntryByIdUseCase.kt deleted file mode 100644 index 3107c6707..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/GetPendingJsonRpcHistoryEntryByIdUseCase.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.walletconnect.chat.json_rpc - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.chat.common.json_rpc.ChatRpc -import com.walletconnect.chat.common.model.JsonRpcHistoryEntry -import com.walletconnect.foundation.common.model.Topic - -internal class GetPendingJsonRpcHistoryEntryByIdUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer, -) { - operator fun invoke(id: Long): JsonRpcHistoryEntry? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) - var entry: JsonRpcHistoryEntry? = null - - if (record != null) { - val chatInvite: ChatRpc.ChatInvite? = serializer.tryDeserialize(record.body) - if (chatInvite != null) { - entry = JsonRpcHistoryEntry.InviteRequest(chatInvite.params, record.id, Topic(record.topic), record.method, record.response) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/JsonRpcMethod.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/JsonRpcMethod.kt deleted file mode 100644 index 0858f1504..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/json_rpc/JsonRpcMethod.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.walletconnect.chat.json_rpc - -internal object JsonRpcMethod { - @get:JvmSynthetic - const val WC_CHAT_INVITE: String = "wc_chatInvite" - @get:JvmSynthetic - const val WC_CHAT_MESSAGE: String = "wc_chatMessage" - @get:JvmSynthetic - const val WC_CHAT_PING: String = "wc_chatPing" - @get:JvmSynthetic - const val WC_CHAT_LEAVE: String = "wc_chatLeave" -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/ChatDidJwtClaims.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/ChatDidJwtClaims.kt deleted file mode 100644 index c72770fd8..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/ChatDidJwtClaims.kt +++ /dev/null @@ -1,106 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.chat.common.model.Media -import com.walletconnect.foundation.util.jwt.JwtClaims - -internal sealed interface ChatDidJwtClaims : JwtClaims { - @JsonClass(generateAdapter = true) - data class RegisterInviteKey( - @Json(name = "iss") override val issuer: String, - @Json(name = "sub") val subject: String, - @Json(name = "aud") val audience: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "pkh") val pkh: String, - @Json(name = "act") val act: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "register_invite" - } - } - - @JsonClass(generateAdapter = true) - data class UnregisterInviteKey( - @Json(name = "iss") override val issuer: String, - @Json(name = "sub") val subject: String, - @Json(name = "aud") val audience: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "pkh") val pkh: String, - @Json(name = "act") val act: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "unregister_invite" - } - } - - @JsonClass(generateAdapter = true) - data class InviteProposal( - @Json(name = "iss") override val issuer: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "ksu") val keyserverUrl: String, - - @Json(name = "aud") val audience: String, // responder/invitee blockchain account (did:pkh) - @Json(name = "sub") val subject: String, // opening message included in the invite - @Json(name = "pke") val inviterPublicKey: String, // proposer/inviter public key (did:key) - @Json(name = "act") val action: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "invite_proposal" - } - } - - @JsonClass(generateAdapter = true) - data class InviteApproval( - @Json(name = "iss") override val issuer: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "ksu") val keyserverUrl: String, - - @Json(name = "aud") val audience: String, // proposer/inviter blockchain account (did:pkh) - @Json(name = "sub") val subject: String, // public key sent by the responder/invitee - @Json(name = "act") val action: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "invite_approval" - } - } - - @JsonClass(generateAdapter = true) - data class ChatMessage( - @Json(name = "iss") override val issuer: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "ksu") val keyserverUrl: String, - - @Json(name = "aud") val audience: String, // recipient blockchain account (did:pkh) - @Json(name = "sub") val subject: String, // message sent by the author account - @Json(name = "xma") val media: Media?, // extensible media attachment (optional) - @Json(name = "act") val action: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "chat_message" - } - } - - @JsonClass(generateAdapter = true) - data class ChatReceipt( - @Json(name = "iss") override val issuer: String, - @Json(name = "iat") val issuedAt: Long, - @Json(name = "exp") val expiration: Long, - @Json(name = "ksu") val keyserverUrl: String, - - @Json(name = "aud") val audience: String, // sender blockchain account (did:pkh) - @Json(name = "sub") val subject: String, // hash of the message received - @Json(name = "act") val action: String = ACT, - ) : ChatDidJwtClaims { - companion object { - const val ACT = "chat_receipt" - } - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatMessageDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatMessageDidJwtPayloadUseCase.kt deleted file mode 100644 index 32963ab73..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatMessageDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,32 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.common.model.Media -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.jwtIatAndExp -import java.util.concurrent.TimeUnit - -internal class EncodeChatMessageDidJwtPayloadUseCase( - private val message: String, - private val recipientAccountId: AccountId, - private val media: Media?, - private val timestampInMs: Long, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.ChatMessage = with(params) { - val (messageIssuedAt, messageExpiration) = jwtIatAndExp(timestampInMs = timestampInMs, timeunit = TimeUnit.MILLISECONDS, expirySourceDuration = 30, expiryTimeUnit = TimeUnit.DAYS) - - return ChatDidJwtClaims.ChatMessage( - issuer = issuer, - issuedAt = messageIssuedAt, - expiration = messageExpiration, - keyserverUrl = keyserverUrl, - subject = message, - audience = encodeDidPkh(recipientAccountId.value), - media = media - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatReceiptDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatReceiptDidJwtPayloadUseCase.kt deleted file mode 100644 index da6c68241..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeChatReceiptDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.util.jwt.encodeDidPkh - -internal class EncodeChatReceiptDidJwtPayloadUseCase( - private val message: String, - private val senderAccountId: AccountId, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.ChatReceipt = with(params) { - ChatDidJwtClaims.ChatReceipt( - issuer = issuer, - issuedAt = issuedAt, - expiration = expiration, - keyserverUrl = keyserverUrl, - subject = sha256(message.toByteArray()), - audience = encodeDidPkh(senderAccountId.value), - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteApprovalDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteApprovalDidJwtPayloadUseCase.kt deleted file mode 100644 index c56e7a1c6..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteApprovalDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeX25519DidKey - -internal class EncodeInviteApprovalDidJwtPayloadUseCase( - private val inviteePublicKey: PublicKey, - private val inviterAccountId: AccountId, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.InviteApproval = with(params) { - ChatDidJwtClaims.InviteApproval( - issuer = issuer, - issuedAt = issuedAt, - expiration = expiration, - keyserverUrl = keyserverUrl, - subject = encodeX25519DidKey(inviteePublicKey.keyAsBytes), - audience = encodeDidPkh(inviterAccountId.value), - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteProposalDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteProposalDidJwtPayloadUseCase.kt deleted file mode 100644 index 8eb7c7892..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeInviteProposalDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeX25519DidKey - -internal class EncodeInviteProposalDidJwtPayloadUseCase( - private val inviterPublicKey: PublicKey, - private val inviteeAccountId: AccountId, - private val openingMessage: String, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.InviteProposal = with(params) { - ChatDidJwtClaims.InviteProposal( - issuer = issuer, - issuedAt = issuedAt, - expiration = expiration, - keyserverUrl = keyserverUrl, - subject = openingMessage, - audience = encodeDidPkh(inviteeAccountId.value), - inviterPublicKey = encodeX25519DidKey(inviterPublicKey.keyAsBytes) - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeRegisterInviteKeyDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeRegisterInviteKeyDidJwtPayloadUseCase.kt deleted file mode 100644 index efa1b04d7..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeRegisterInviteKeyDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.util.jwt.encodeDidPkh - -internal class EncodeRegisterInviteKeyDidJwtPayloadUseCase( - private val invitePublicKey: String, - private val accountId: AccountId, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.RegisterInviteKey = with(params) { - ChatDidJwtClaims.RegisterInviteKey( - issuer = issuer, - issuedAt = issuedAt, - expiration = expiration, - audience = keyserverUrl, - subject = invitePublicKey, - pkh = encodeDidPkh(accountId.value) - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeUnregisterInviteKeyDidJwtPayloadUseCase.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeUnregisterInviteKeyDidJwtPayloadUseCase.kt deleted file mode 100644 index ee14b05a2..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/jwt/use_case/EncodeUnregisterInviteKeyDidJwtPayloadUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.jwt.use_case - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.jwt.ChatDidJwtClaims -import com.walletconnect.foundation.util.jwt.encodeDidPkh - -internal class EncodeUnregisterInviteKeyDidJwtPayloadUseCase( - private val invitePublicKey: String, - private val accountId: AccountId, -) : EncodeDidJwtPayloadUseCase { - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): ChatDidJwtClaims.UnregisterInviteKey = with(params) { - ChatDidJwtClaims.UnregisterInviteKey( - issuer = issuer, - issuedAt = issuedAt, - expiration = expiration, - audience = keyserverUrl, - subject = invitePublicKey, - pkh = encodeDidPkh(accountId.value) - ) - } -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/AccountsStorageRepository.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/AccountsStorageRepository.kt deleted file mode 100644 index efcd9f334..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/AccountsStorageRepository.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.walletconnect.chat.storage - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.common.model.Account -import com.walletconnect.chat.storage.data.dao.AccountsQueries -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -internal class AccountsStorageRepository(private val accounts: AccountsQueries) { - - private suspend fun doesAccountNotExists(accountId: AccountId) = accounts.doesAccountNotExists(accountId.value).executeAsOne() - - suspend fun upsertAccount(account: Account) = - if (doesAccountNotExists(account.accountId)) { - createAccount(account) - } else { - updateAccount(account) - } - - private suspend fun updateAccount(account: Account) = with(account) { - accounts.updateAccount(publicIdentityKey.keyAsHex, accountId.value) - } - - private suspend fun createAccount(account: Account) = with(account) { - accounts.insertOrAbortAccount(accountId = accountId.value, publicIdentityKey = publicIdentityKey.keyAsHex, publicInviteKey = publicInviteKey?.keyAsHex, inviteTopic = inviteTopic?.value) - } - - suspend fun getAllInviteTopics(): List = - accounts.getAllInviteTopics().executeAsList().map { dbToTopic(it) } - - suspend fun getAccountByAccountId(accountId: AccountId): Account = - accounts.getAccountByAccountId(accountId.value, ::dbToAccount).executeAsOne() - - suspend fun getAccountByInviteTopic(inviteTopic: Topic): Account = - accounts.getAccountByInviteTopic(inviteTopic.value, ::dbToAccount).executeAsOne() - - suspend fun deleteAccountByAccountId(accountId: AccountId) = - accounts.deleteAccountByAccountId(accountId.value) - - suspend fun setAccountPublicInviteKey(accountId: AccountId, publicInviteKey: PublicKey?, inviteTopic: Topic?) = - accounts.updateAccountPublicInviteKey(publicInviteKey?.keyAsHex, inviteTopic?.value, accountId.value) - - suspend fun removeAccountPublicInviteKey(accountId: AccountId) = - accounts.removeAccountPublicInviteKey(accountId.value) - - private fun dbToTopic(inviteTopic: String) = Topic(inviteTopic) - - private fun dbToAccount(accountId: String, publicIdentityKey: String, publicInviteKey: String?, inviteTopic: String?): Account = - Account(AccountId(accountId), PublicKey(publicIdentityKey), publicInviteKey?.let { PublicKey(publicInviteKey) }, inviteTopic?.let { Topic(inviteTopic) }) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ContactStorageRepository.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ContactStorageRepository.kt deleted file mode 100644 index 5a554cba7..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ContactStorageRepository.kt +++ /dev/null @@ -1,44 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.chat.storage - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.common.model.Contact -import com.walletconnect.chat.storage.data.dao.ContactsQueries -import com.walletconnect.foundation.common.model.PublicKey - -internal class ContactStorageRepository(private val contactsQueries: ContactsQueries) { - - @JvmSynthetic - internal suspend fun doesContactNotExists(accountIdVO: AccountId): Boolean = - contactsQueries.doesContactNotExists(accountIdVO.value).executeAsOne() - - @JvmSynthetic - internal suspend fun upsertContact(contact: Contact) = with(contact) { - if (doesContactNotExists(accountId)) { - createContact(contact) - } else { - updateContact(contact) - } - } - - @JvmSynthetic - internal suspend fun createContact(contact: Contact) = contactsQueries.insertOrAbortContact( - contact.accountId.value, - contact.publicKey.keyAsHex, - contact.displayName - ) - - @JvmSynthetic - internal suspend fun getContact(accountId: AccountId): Contact = - contactsQueries.getContact(accountId.value, mapper = ::mapContactDaoToContact).executeAsOne() - - @JvmSynthetic - internal suspend fun updateContact(contact: Contact) = with(contact) { - contactsQueries.updateContactPublicKey(publicKey.keyAsHex, accountId.value) - contactsQueries.updateContactDisplayName(displayName, accountId.value) - } - - private fun mapContactDaoToContact(account_id: String, public_key: String, display_name: String): Contact = - Contact(AccountId(account_id), PublicKey(public_key), display_name) -} \ No newline at end of file diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/InvitesStorageRepository.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/InvitesStorageRepository.kt deleted file mode 100644 index cb12d85f0..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/InvitesStorageRepository.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.walletconnect.chat.storage - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.chat.common.model.Invite -import com.walletconnect.chat.common.model.InviteMessage -import com.walletconnect.chat.common.model.InviteStatus -import com.walletconnect.chat.common.model.InviteType -import com.walletconnect.chat.storage.data.dao.InvitesQueries -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.utils.Empty - -internal class InvitesStorageRepository(private val invites: InvitesQueries) { - suspend fun insertInvite(invite: Invite) { - val type = when (invite) { - is Invite.Received -> InviteType.RECEIVED - is Invite.Sent -> InviteType.SENT - } - - if (type == InviteType.SENT && invite.inviterPrivateKey == null) throw Throwable("Invite.Sent requires invitePrivateKey to not be null") - - val inviterPrivateKey = if (type == InviteType.SENT) invite.inviterPrivateKey!!.keyAsHex else String.Empty - - with(invite) { - invites.insertOrAbortInvite( - inviteId = id, message = message.value, inviterAccount = inviterAccount.value, - inviteeAccount = inviteeAccount.value, status = InviteStatus.PENDING, type = type, acceptTopic = acceptTopic.value, - inviterPublicKey = inviterPublicKey.keyAsHex, symmetricKey = symmetricKey.keyAsHex, - inviterPrivateKey = inviterPrivateKey, timestamp = timestamp - ) - } - } - - suspend fun checkIfAccountsHaveExistingInvite(inviterAccount: String, inviteeAccount: String): Boolean = invites.checkIfAccountsHaveExistingInvite(inviterAccount, inviteeAccount).executeAsOne() - - suspend fun getAllPendingSentInvites() = invites.getAllPendingSentInvites(::dbToSentInvite).executeAsList() - - suspend fun deleteInviteByInviteId(inviteId: Long) = invites.deleteInviteByInviteId(inviteId) - - suspend fun getReceivedInviteByInviteId(inviteId: Long) = invites.getInviteByInviteId(inviteId, ::dbToReceivedInvite).executeAsOne() - - suspend fun getSentInviteByInviteId(inviteId: Long) = invites.getInviteByInviteId(inviteId, ::dbToSentInvite).executeAsOne() - - suspend fun updateStatusByInviteId(inviteId: Long, status: InviteStatus) = invites.updateInviteStatusByInviteId(status, inviteId) - - suspend fun updateStatusByAccounts(accountOne: String, accountTwo: String, status: InviteStatus) { - // When it's impossible to distinct which account is the inviter try updating two - // Very unlikely non-hostile drawback: When both accounts are register within Chat SDK and they invite each other first out of two updates will update two invites: SENT and RECEIVED - invites.updateInviteStatusByAccounts(status, accountOne, accountTwo) - invites.updateInviteStatusByAccounts(status, accountTwo, accountOne) - } - - suspend fun getSentInvitesForInviterAccount(inviterAccount: String): List = invites.getSentInvitesForInviterAccount(inviterAccount, ::dbToSentInvite).executeAsList() - - suspend fun getReceivedInvitesForInviteeAccount(inviterAccount: String): List = invites.getReceivedInvitesForInviteeAccount(inviterAccount, ::dbToReceivedInvite).executeAsList() - - private fun dbToSentInvite( - inviteId: Long, message: String, inviterAccount: String, inviteeAccount: String, - status: InviteStatus, inviterPublicKey: String, acceptTopic: String, symmetricKey: String, inviterPrivateKey: String, timestamp: Long, - ): Invite.Sent = Invite.Sent( - id = inviteId, - inviterAccount = AccountId(inviterAccount), - inviteeAccount = AccountId(inviteeAccount), - message = InviteMessage(message), - inviterPublicKey = PublicKey(inviterPublicKey), - status = status, - acceptTopic = Topic(acceptTopic), - symmetricKey = SymmetricKey(symmetricKey), - inviterPrivateKey = PrivateKey(inviterPrivateKey), - timestamp = timestamp, - ) - - private fun dbToReceivedInvite( - inviteId: Long, message: String, inviterAccount: String, inviteeAccount: String, - status: InviteStatus, inviterPublicKey: String, acceptTopic: String, symmetricKey: String, inviterPrivateKey: String, // unused but required by select query - timestamp: Long, - ): Invite.Received = Invite.Received( - id = inviteId, - inviterAccount = AccountId(inviterAccount), - inviteeAccount = AccountId(inviteeAccount), - message = InviteMessage(message), - inviterPublicKey = PublicKey(inviterPublicKey), - status = status, - acceptTopic = Topic(acceptTopic), - symmetricKey = SymmetricKey(symmetricKey), - inviterPrivateKey = null, - timestamp = timestamp, - ) -} diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/MessageStorageRepository.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/MessageStorageRepository.kt deleted file mode 100644 index bb768d203..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/MessageStorageRepository.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.chat.storage - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.common.model.* -import com.walletconnect.chat.storage.data.dao.MessagesQueries -import com.walletconnect.foundation.common.model.Topic - -internal class MessageStorageRepository(private val messages: MessagesQueries) { - - suspend fun insertMessage(message: Message) = with(message) { - messages.insertOrAbortMessage( - messageId = messageId, topic = topic.value, message = message.message.value, authorAccount = authorAccount.value, - timestamp = timestamp, mediaType = media?.type, mediaData = media?.data?.value - ) - } - - suspend fun deleteMessageByMessageId(messageId: Long) = messages.deleteMessageByMessageId(messageId) - - suspend fun deleteMessagesByTopic(topic: String) = messages.deleteMessagesByTopic(topic) - - suspend fun getMessageByTopic(topic: String): List = messages.getMessagesByTopic(topic, ::dbToMessage).executeAsList() - - private fun dbToMessage(messageId: Long, topic: String, message: String, authorAccount: String, timestamp: Long, mediaType: String?, mediaData: String?) = - Message(messageId, Topic(topic), ChatMessage(message), AccountId(authorAccount), timestamp, if (mediaType != null && mediaData != null) Media(mediaType, MediaData(mediaData)) else null) - -} diff --git a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ThreadsStorageRepository.kt b/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ThreadsStorageRepository.kt deleted file mode 100644 index 4cb7e30e4..000000000 --- a/protocol/chat/src/main/kotlin/com/walletconnect/chat/storage/ThreadsStorageRepository.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.walletconnect.chat.storage - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.chat.common.model.Thread -import com.walletconnect.chat.storage.data.dao.ThreadsQueries -import com.walletconnect.foundation.common.model.Topic - -internal class ThreadsStorageRepository(private val threads: ThreadsQueries) { - suspend fun insertThread(topic: String, selfAccount: String, peerAccount: String) = threads.insertOrAbortThread(topic, selfAccount, peerAccount) - - suspend fun getThreadsForSelfAccount(account: String): List = threads.getThreadsForSelfAccount(account, ::dbToThread).executeAsList() - - suspend fun checkIfSelfAccountHaveThreadWithPeerAccount(selfAccount: String, peerAccount: String): Boolean = - threads.checkIfSelfAccountHaveThreadWithPeerAccount(selfAccount, peerAccount).executeAsOne() - - suspend fun deleteThreadByTopic(topic: String) = threads.deleteThreadByTopic(topic) - - suspend fun getThreadByTopic(topic: String): Thread = threads.getThreadByTopic(topic, ::dbToThread).executeAsOne() - - suspend fun getAllThreads(): List = threads.getAllThreads(::dbToThread).executeAsList() - - private fun dbToThread(topic: String, selfPublicKey: String, peerPublicKey: String) = Thread(Topic(topic), AccountId(selfPublicKey), AccountId(peerPublicKey)) -} \ No newline at end of file diff --git a/protocol/chat/src/main/res/xml/backup_rules.xml b/protocol/chat/src/main/res/xml/backup_rules.xml deleted file mode 100644 index e72e65165..000000000 --- a/protocol/chat/src/main/res/xml/backup_rules.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Accounts.sq b/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Accounts.sq deleted file mode 100644 index b9f81af07..000000000 --- a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Accounts.sq +++ /dev/null @@ -1,53 +0,0 @@ -CREATE TABLE Accounts ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - accountId TEXT UNIQUE NOT NULL, - publicIdentityKey TEXT NOT NULL, - publicInviteKey TEXT, - inviteTopic TEXT UNIQUE -); - -insertOrAbortAccount: -INSERT OR ABORT INTO Accounts(accountId, publicIdentityKey, publicInviteKey, inviteTopic) -VALUES (?, ?, ?, ?); - -getAccountByAccountId: -SELECT accountId, publicIdentityKey, publicInviteKey, inviteTopic -FROM Accounts -WHERE accountId = ?; - -getAccountByInviteTopic: -SELECT accountId, publicIdentityKey, publicInviteKey, inviteTopic -FROM Accounts -WHERE inviteTopic = ?; - -deleteAccountByAccountId: -DELETE FROM Accounts -WHERE accountId = ?; - -updateAccountPublicInviteKey: -UPDATE Accounts -SET publicInviteKey = ?, inviteTopic = ? -WHERE accountId = ?; - -updateAccount: -UPDATE Accounts -SET publicIdentityKey = ? -WHERE accountId = ?; - -getAllInviteTopics: -SELECT inviteTopic -FROM Accounts -WHERE inviteTopic IS NOT NULL; - -removeAccountPublicInviteKey: -UPDATE Accounts -SET publicInviteKey = NULL, inviteTopic = NULL -WHERE accountId = ?; - -doesAccountNotExists: -SELECT NOT EXISTS ( - SELECT 1 - FROM Accounts - WHERE accountId = ? - LIMIT 1 -); diff --git a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Contacts.sq b/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Contacts.sq deleted file mode 100644 index 99f260e2c..000000000 --- a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Contacts.sq +++ /dev/null @@ -1,37 +0,0 @@ -CREATE TABLE Contacts( - account_id TEXT PRIMARY KEY NOT NULL, - public_key TEXT NOT NULL, - display_name TEXT NOT NULL -); - -insertOrAbortContact: -INSERT OR ABORT INTO Contacts (account_id, public_key, display_name) -VALUES (?, ?, ?); - -updateContactPublicKey: -UPDATE Contacts -SET public_key = ? -WHERE account_id = ?; - -updateContactDisplayName: -UPDATE Contacts -SET display_name = ? -WHERE account_id = ?; - -getContact: -SELECT account_id, public_key, display_name -FROM Contacts -WHERE account_id = ? -LIMIT 1; - -doesContactNotExists: -SELECT NOT EXISTS ( - SELECT 1 - FROM Contacts - WHERE account_id = ? - LIMIT 1 -); - -deleteContact: -DELETE FROM Contacts -WHERE account_id = ?; \ No newline at end of file diff --git a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Invites.sq b/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Invites.sq deleted file mode 100644 index b02bf6db7..000000000 --- a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Invites.sq +++ /dev/null @@ -1,66 +0,0 @@ -import com.walletconnect.chat.common.model.InviteStatus; -import com.walletconnect.chat.common.model.InviteType; - -CREATE TABLE Invites ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - inviteId INTEGER UNIQUE NOT NULL, - message TEXT NOT NULL, - inviterAccount TEXT NOT NULL, - inviteeAccount TEXT NOT NULL, - status TEXT AS InviteStatus NOT NULL, -- PENDING, REJECTED OR APPROVED - type TEXT AS InviteType NOT NULL, -- SENT OR RECEIVED - inviterPublicKey TEXT NOT NULL, - acceptTopic TEXT NOT NULL, - symmetricKey TEXT NOT NULL, - inviterPrivateKey TEXT NOT NULL, - timestamp INTEGER NOT NULL, - UNIQUE(inviterAccount, inviteeAccount) -); - -insertOrAbortInvite: -INSERT OR ABORT INTO Invites(inviteId, message, inviterAccount, inviteeAccount, status, type, inviterPublicKey, acceptTopic, symmetricKey, inviterPrivateKey, timestamp) -VALUES (?, ?, ?, ?, ?,?, ?, ?, ?, ?,?); - -checkIfAccountsHaveExistingInvite: -SELECT EXISTS ( - SELECT 1 - FROM Invites - WHERE inviterAccount = ? AND inviteeAccount = ? - LIMIT 1 -); - -updateInviteStatusByInviteId: -UPDATE Invites -SET status = ? -WHERE inviteId = ?; - -updateInviteStatusByAccounts: -UPDATE Invites -SET status = ? -WHERE inviterAccount = ? AND inviteeAccount = ?; - -deleteInviteByInviteId: -DELETE FROM Invites -WHERE inviteId = ?; - -getInviteByInviteId: -SELECT inviteId, message, inviterAccount, inviteeAccount, status, inviterPublicKey, acceptTopic, symmetricKey, inviterPrivateKey, timestamp -FROM Invites -WHERE inviteId = ?; - -getAllPendingSentInvites: -SELECT inviteId, message, inviterAccount, inviteeAccount, status, inviterPublicKey, acceptTopic, symmetricKey, inviterPrivateKey, timestamp -FROM Invites -WHERE status = 'PENDING' AND type = 'SENT'; - --- SentInvites -getSentInvitesForInviterAccount: -SELECT inviteId, message, inviterAccount, inviteeAccount, status, inviterPublicKey, acceptTopic, symmetricKey, inviterPrivateKey, timestamp -FROM Invites -WHERE inviterAccount = ? AND type = 'SENT'; - --- ReceivedInvites -getReceivedInvitesForInviteeAccount: -SELECT inviteId, message, inviterAccount, inviteeAccount, status, inviterPublicKey, acceptTopic, symmetricKey, inviterPrivateKey, timestamp -FROM Invites -WHERE inviteeAccount = ? AND type = 'RECEIVED'; \ No newline at end of file diff --git a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Messages.sq b/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Messages.sq deleted file mode 100644 index e206fea27..000000000 --- a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Messages.sq +++ /dev/null @@ -1,27 +0,0 @@ -CREATE TABLE Messages ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - messageId INTEGER UNIQUE NOT NULL, - topic TEXT NOT NULL, - message TEXT NOT NULL, - authorAccount TEXT NOT NULL, - timestamp INTEGER NOT NULL, - mediaType TEXT, - mediaData TEXT -); - -insertOrAbortMessage: -INSERT OR ABORT INTO Messages(topic, messageId, message, authorAccount,timestamp, mediaType, mediaData) -VALUES (?, ?, ?, ?, ? ,? ,?); - -getMessagesByTopic: -SELECT messageId, topic, message, authorAccount, timestamp, mediaType, mediaData -FROM Messages -WHERE topic = ?; - -deleteMessagesByTopic: -DELETE FROM Messages -WHERE topic = ?; - -deleteMessageByMessageId: -DELETE FROM Messages -WHERE messageId = ?; \ No newline at end of file diff --git a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Threads.sq b/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Threads.sq deleted file mode 100644 index a68181e00..000000000 --- a/protocol/chat/src/main/sqldelight/com/walletconnect/chat/storage/data/dao/Threads.sq +++ /dev/null @@ -1,37 +0,0 @@ -CREATE TABLE Threads ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - topic TEXT UNIQUE NOT NULL, - selfAccount TEXT NOT NULL, - peerAccount TEXT NOT NULL, - UNIQUE(selfAccount, peerAccount) -); - -insertOrAbortThread: -INSERT OR ABORT INTO Threads(topic, selfAccount, peerAccount) -VALUES (?, ?, ?); - -getThreadsForSelfAccount: -SELECT topic, selfAccount, peerAccount -FROM Threads -WHERE selfAccount = ?; - -checkIfSelfAccountHaveThreadWithPeerAccount: -SELECT EXISTS ( - SELECT 1 - FROM Threads - WHERE selfAccount = ? AND peerAccount = ? - LIMIT 1 -); - -getThreadByTopic: -SELECT topic, selfAccount, peerAccount -FROM Threads -WHERE topic = ?; - -getAllThreads: -SELECT topic, selfAccount, peerAccount -FROM Threads; - -deleteThreadByTopic: -DELETE FROM Threads -WHERE topic = ?; \ No newline at end of file diff --git a/protocol/chat/src/main/sqldelight/migration/1.sqm b/protocol/chat/src/main/sqldelight/migration/1.sqm deleted file mode 100644 index 31e3fce2d..000000000 --- a/protocol/chat/src/main/sqldelight/migration/1.sqm +++ /dev/null @@ -1,32 +0,0 @@ -import com.walletconnect.chat.common.model.InviteStatus; -import com.walletconnect.chat.common.model.InviteType; - --- migration from 1.db to 2.db - -DROP TABLE IF EXISTS Invites; -DROP TABLE IF EXISTS Threads; - -CREATE TABLE Invites ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - inviteId INTEGER UNIQUE NOT NULL, - message TEXT NOT NULL, - inviterAccount TEXT NOT NULL, - inviteeAccount TEXT NOT NULL, - status TEXT AS InviteStatus NOT NULL, -- PENDING, REJECTED OR APPROVED - type TEXT AS InviteType NOT NULL, -- SENT OR RECEIVED - inviterPublicKey TEXT NOT NULL, - acceptTopic TEXT NOT NULL, - symmetricKey TEXT NOT NULL, - inviterPrivateKey TEXT NOT NULL, - timestamp INTEGER NOT NULL, - UNIQUE(inviterAccount, inviteeAccount) -); - - -CREATE TABLE Threads ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - topic TEXT UNIQUE NOT NULL, - selfAccount TEXT NOT NULL, - peerAccount TEXT NOT NULL, - UNIQUE(selfAccount, peerAccount) -); \ No newline at end of file diff --git a/protocol/chat/src/test/kotlin/com/walletconnect/chat/jwt/DidJwtRepositoryTest.kt b/protocol/chat/src/test/kotlin/com/walletconnect/chat/jwt/DidJwtRepositoryTest.kt deleted file mode 100644 index feebb56b2..000000000 --- a/protocol/chat/src/test/kotlin/com/walletconnect/chat/jwt/DidJwtRepositoryTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.walletconnect.chat.jwt - -import android.util.Log -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import io.mockk.every -import io.mockk.mockkStatic -import junit.framework.TestCase.assertNotNull -import org.junit.Test - -internal class DidJwtRepositoryTest { - - init { - mockkStatic(Log::class) - every { Log.v(any(), any()) } returns 0 - every { Log.d(any(), any()) } returns 0 - every { Log.i(any(), any()) } returns 0 - every { Log.e(any(), any()) } returns 0 - } - - @Test - fun jwtDecode() { - val jwt = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9" + - ".eyJpc3MiOiJkaWQ6a2V5Ono2TWt1VHhCUTR5aTN1Y3pUWlJUYkxKVFNLaGUzN2phUnJzdEJpUWg2SmIxZ3QzWCIsImlhdCI6MTY3NTQzMjI2NiwiZXhwIjoxNjc1NTE4NjY2LCJrc3UiOiJodHRwczovL3N0YWdpbmcua2V5cy53YWxsZXRjb25uZWN0LmNvbSIsImF1ZCI6ImRpZDpwa2g6ZWlwMTU1OjE6MHhlN2M3M2JhNjlhZGM2NTkzZDM3YmQ2ZmUwMDk5NWZiODljNTQxMjg2Iiwic3ViIjoiSGV5ISIsInBrZSI6ImRpZDprZXk6ejZMU21CQ0JLd0NaemJDd3J2RWFpdFU5TFJNMnRWOWYzWHVVN3FzTUJ4bkN6U1RrIn0.NdalJAlosJygMG4gsh0ZgKtVLvEm473E4cBRmqkvZX_M1KahHuyk59ZEB9VMnqvXjnmbfMEcMvKt-unRhNsDDw" - val t = extractVerifiedDidJwtClaims(jwt).getOrNull() - assertNotNull(t) - } -} \ No newline at end of file diff --git a/protocol/notify/build.gradle.kts b/protocol/notify/build.gradle.kts index ac98ab1e1..84f6571f1 100644 --- a/protocol/notify/build.gradle.kts +++ b/protocol/notify/build.gradle.kts @@ -14,7 +14,7 @@ project.apply { } android { - namespace = "com.walletconnect.notify" + namespace = "com.reown.notify" compileSdk = COMPILE_SDK defaultConfig { @@ -74,7 +74,7 @@ android { sqldelight { databases { create("NotifyDatabase") { - packageName.set("com.walletconnect.notify") + packageName.set("com.reown.notify") schemaOutputDirectory.set(file("src/main/sqldelight/databases")) // generateAsync.set(true) TODO uncomment once all SDKs have this flag enabled verifyMigrations.set(true) @@ -85,7 +85,7 @@ sqldelight { dependencies { debugImplementation(project(":core:android")) - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") + releaseImplementation("com.reown:android-core:$CORE_VERSION") implementation("com.squareup.retrofit2:converter-scalars:2.9.0") ksp(libs.moshi.ksp) diff --git a/protocol/notify/src/androidTest/AndroidManifest.xml b/protocol/notify/src/androidTest/AndroidManifest.xml index f2be8ec9e..ddbb8e4e1 100644 --- a/protocol/notify/src/androidTest/AndroidManifest.xml +++ b/protocol/notify/src/androidTest/AndroidManifest.xml @@ -7,7 +7,7 @@ diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt new file mode 100644 index 000000000..64549d500 --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt @@ -0,0 +1,40 @@ +package com.reown.notify.di + +import com.reown.android.di.coreStorageModule +import com.reown.android.internal.common.di.coreAndroidNetworkModule +import com.reown.android.internal.common.di.coreCryptoModule +import com.reown.android.internal.common.di.coreJsonRpcModule +import com.reown.android.internal.common.di.corePairingModule +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayConnectionInterface +import org.koin.dsl.module + +private const val SHARED_PREFS_FILE = "wc_key_store" +private const val KEY_STORE_ALIAS = "wc_keystore_key" + +// When called more that once, different `storagePrefix` must be defined. +@JvmSynthetic +internal fun overrideModule( + relay: RelayConnectionInterface, + pairing: PairingInterface, + pairingController: PairingControllerInterface, + storagePrefix: String, + relayUrl: String, + connectionType: ConnectionType, + bundleId: String +) = module { + val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE + val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS + + single { relay } + + includes( + coreStorageModule(storagePrefix, bundleId), + corePairingModule(pairing, pairingController), + coreCryptoModule(sharedPrefsFile, keyStoreAlias), + coreJsonRpcModule(), + coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId) + ) +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/activity/InstrumentedTestActivity.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/activity/InstrumentedTestActivity.kt new file mode 100644 index 000000000..16d6db5d8 --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/activity/InstrumentedTestActivity.kt @@ -0,0 +1,11 @@ +package com.reown.notify.test.activity + +import android.app.Activity +import android.os.Bundle + +class InstrumentedTestActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/client/NotifyClientInstrumentedAndroidTest.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/client/NotifyClientInstrumentedAndroidTest.kt similarity index 93% rename from protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/client/NotifyClientInstrumentedAndroidTest.kt rename to protocol/notify/src/androidTest/kotlin/com/reown/notify/test/client/NotifyClientInstrumentedAndroidTest.kt index a39ee70fe..68f871230 100644 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/client/NotifyClientInstrumentedAndroidTest.kt +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/client/NotifyClientInstrumentedAndroidTest.kt @@ -1,15 +1,15 @@ -package com.walletconnect.notify.test.client +package com.reown.notify.test.client import androidx.core.net.toUri -import com.walletconnect.notify.BuildConfig -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyInterface -import com.walletconnect.notify.test.scenario.ClientInstrumentedActivityScenario -import com.walletconnect.notify.test.utils.TestClient -import com.walletconnect.notify.test.utils.primary.PrimaryNotifyClient -import com.walletconnect.notify.test.utils.primary.PrimaryNotifyDelegate -import com.walletconnect.notify.test.utils.secondary.SecondaryNotifyClient -import com.walletconnect.notify.test.utils.secondary.SecondaryNotifyDelegate +import com.reown.notify.BuildConfig +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyInterface +import com.reown.notify.test.scenario.ClientInstrumentedActivityScenario +import com.reown.notify.test.utils.TestClient +import com.reown.notify.test.utils.primary.PrimaryNotifyClient +import com.reown.notify.test.utils.primary.PrimaryNotifyDelegate +import com.reown.notify.test.utils.secondary.SecondaryNotifyClient +import com.reown.notify.test.utils.secondary.SecondaryNotifyDelegate import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import okhttp3.Headers diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ActivityScenario.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ActivityScenario.kt similarity index 93% rename from protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ActivityScenario.kt rename to protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ActivityScenario.kt index 140b390ce..a76785a09 100644 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ActivityScenario.kt +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ActivityScenario.kt @@ -1,8 +1,8 @@ -package com.walletconnect.notify.test.scenario +package com.reown.notify.test.scenario import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario -import com.walletconnect.notify.test.activity.InstrumentedTestActivity +import com.reown.notify.test.activity.InstrumentedTestActivity import junit.framework.TestCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ClientInstrumentedActivityScenario.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ClientInstrumentedActivityScenario.kt similarity index 93% rename from protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ClientInstrumentedActivityScenario.kt rename to protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ClientInstrumentedActivityScenario.kt index 3faccbf63..4320bcd76 100644 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/scenario/ClientInstrumentedActivityScenario.kt +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/scenario/ClientInstrumentedActivityScenario.kt @@ -1,12 +1,12 @@ -package com.walletconnect.notify.test.scenario - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.test.BuildConfig -import com.walletconnect.notify.test.utils.TestClient +package com.reown.notify.test.scenario + +import com.reown.android.Core +import com.reown.android.internal.common.scope +import com.reown.android.relay.WSSConnectionState +import com.reown.foundation.network.model.Relay +import com.reown.notify.client.Notify +import com.reown.notify.test.BuildConfig +import com.reown.notify.test.utils.TestClient import junit.framework.TestCase.fail import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/Common.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/Common.kt new file mode 100644 index 000000000..d6c0a7747 --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/Common.kt @@ -0,0 +1,16 @@ +package com.reown.notify.test.utils + +import com.reown.android.Core +import com.reown.notify.client.Notify +import junit.framework.TestCase.fail +import timber.log.Timber + +internal fun globalOnError(error: Notify.Model.Error) { + Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") + fail(error.throwable.message) +} + +internal fun globalOnError(error: Core.Model.Error) { + Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") + fail(error.throwable.message) +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/TestClient.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/TestClient.kt new file mode 100644 index 000000000..2f1102d77 --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/TestClient.kt @@ -0,0 +1,163 @@ +package com.reown.notify.test.utils + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.CoreProtocol +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayClient +import com.reown.android.utils.cacao.sign +import com.reown.foundation.common.model.PrivateKey +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.notify.client.NotifyProtocol +import com.reown.notify.client.cacao.CacaoSigner +import com.reown.notify.di.overrideModule +import com.reown.notify.test.BuildConfig +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.koin.core.KoinApplication +import timber.log.Timber + +internal object TestClient { + private val app = ApplicationProvider.getApplicationContext() + fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = init().apply { createEagerInstances() } + + internal const val account = "0x29163BFD38865971EfA5896e63bd6b7C8370E557" + internal const val caip10account = "eip155:1:$account" + internal const val publicKey = "5112cfe94b391100464b51af6f6714b4d7c21d25c92360b20b07278e722e033d426d26779f413330ceaf7cec2d5b4db91fe11bbb222955272f48478346549fe4" + internal val privateKey = PrivateKey("7191ca005ee3696cf1f9d1f35d3baabaee55188db12fca85f662af7cc181f896") + + object Primary { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Notify Primary", + description = "Notify Primary Client for automation tests", + url = "kotlin.e2e.notify.primary", + icons = listOf(), + redirect = null + ) + + private val coreProtocol = CoreClient.apply { + Timber.d("Primary CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) + Relay.connect(::globalOnError) + } + + private val initParams = Notify.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + + internal val notifyClient = + NotifyClient + .apply { + initialize(initParams, onError = { Timber.e(it.throwable) }) + }.also { notifyClient -> + + val isRegistered = notifyClient.isRegistered(params = Notify.Params.IsRegistered(caip10account, metadata.url)) + + if (!isRegistered) { + notifyClient.prepareRegistration(Notify.Params.PrepareRegistration(caip10account, metadata.url), + onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> + Timber.d("PrepareRegistration Success") + + val signature = CacaoSigner.sign(message, privateKey.keyAsBytes, SignatureType.EIP191) + notifyClient.register( + params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), + onSuccess = { identityKey -> + Timber.d("Primary CP finish: $identityKey") + _isInitialized.tryEmit(true) + }, + onError = { Timber.e(it.throwable.stackTraceToString()) } + ) + + }, + onError = { error -> + Timber.e(error.throwable) + throw error.throwable + }) + } + } + + internal val Relay get() = coreProtocol.Relay + } + + object Secondary { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Notify Secondary", + description = "Notify Secondary Client for automation tests", + url = "kotlin.e2e.notify.secondary", + icons = listOf(), + redirect = null + ) + + private val secondaryKoinApp = KoinApplication.createNewWCKoinApp() + + private val coreProtocol = CoreProtocol(secondaryKoinApp).apply { + Timber.d("Secondary CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } + + // Override of previous Relay necessary for reinitialization of `eventsFlow` + Relay = RelayClient(secondaryKoinApp) + + // Override of storage instances and depending objects + secondaryKoinApp.modules( + overrideModule( + Relay, + Pairing, + PairingController, + "test_secondary", + "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", + ConnectionType.MANUAL, + app.packageName + ) + ) + + // Necessary reinit of Relay, Pairing and PairingController + Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } + Pairing.initialize() + PairingController.initialize() + + Relay.connect(::globalOnError) + } + + private val initParams = Notify.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + + internal val notifyClient = NotifyProtocol(secondaryKoinApp).apply { + initialize(initParams, onError = { Timber.e(it.throwable) }) + }.also { notifyClient -> + val isRegistered = notifyClient.isRegistered(params = Notify.Params.IsRegistered(caip10account, metadata.url)) + + + if (!isRegistered) { + notifyClient.prepareRegistration(Notify.Params.PrepareRegistration(caip10account, metadata.url), + onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> + Timber.d("PrepareRegistration Success") + + val signature = CacaoSigner.sign(message, privateKey.keyAsBytes, SignatureType.EIP191) + + notifyClient.register( + params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), + onSuccess = { identityKey -> + Timber.d("Secondary CP finish: $identityKey") + _isInitialized.tryEmit(true) + }, + onError = { Timber.e(it.throwable.stackTraceToString()) } + ) + + }, + onError = { error -> + Timber.e(error.throwable) + throw error.throwable + }) + } + } + + internal val Relay get() = coreProtocol.Relay + } +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyClient.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyClient.kt new file mode 100644 index 000000000..10eaafe2d --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyClient.kt @@ -0,0 +1,5 @@ +package com.reown.notify.test.utils.primary + +import com.reown.notify.test.utils.TestClient + +val PrimaryNotifyClient = TestClient.Primary.notifyClient diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyDelegate.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyDelegate.kt new file mode 100644 index 000000000..f8e6f085a --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/primary/PrimaryNotifyDelegate.kt @@ -0,0 +1,18 @@ +package com.reown.notify.test.utils.primary + +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyInterface +import com.reown.notify.test.utils.globalOnError + +open class PrimaryNotifyDelegate : NotifyInterface.Delegate { + + override fun onNotifyNotification(notifyNotification: Notify.Event.Notification) { + } + + override fun onSubscriptionsChanged(subscriptionsChanged: Notify.Event.SubscriptionsChanged) { + } + + override fun onError(error: Notify.Model.Error) { + globalOnError(error) + } +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyClient.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyClient.kt new file mode 100644 index 000000000..18d380c06 --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyClient.kt @@ -0,0 +1,5 @@ +package com.reown.notify.test.utils.secondary + +import com.reown.notify.test.utils.TestClient + +val SecondaryNotifyClient = TestClient.Secondary.notifyClient \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyDelegate.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyDelegate.kt new file mode 100644 index 000000000..691abc9aa --- /dev/null +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/test/utils/secondary/SecondaryNotifyDelegate.kt @@ -0,0 +1,18 @@ +package com.reown.notify.test.utils.secondary + +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyInterface +import com.reown.notify.test.utils.globalOnError + +open class SecondaryNotifyDelegate : NotifyInterface.Delegate { + + override fun onNotifyNotification(notifyNotification: Notify.Event.Notification) { + } + + override fun onSubscriptionsChanged(subscriptionsChanged: Notify.Event.SubscriptionsChanged) { + } + + override fun onError(error: Notify.Model.Error) { + globalOnError(error) + } +} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/di/OverrideModule.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/di/OverrideModule.kt deleted file mode 100644 index 1aec2d573..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/di/OverrideModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.walletconnect.notify.di - -import com.walletconnect.android.di.coreStorageModule -import com.walletconnect.android.internal.common.di.coreAndroidNetworkModule -import com.walletconnect.android.internal.common.di.coreCryptoModule -import com.walletconnect.android.internal.common.di.coreJsonRpcModule -import com.walletconnect.android.internal.common.di.corePairingModule -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayConnectionInterface -import org.koin.dsl.module - -private const val SHARED_PREFS_FILE = "wc_key_store" -private const val KEY_STORE_ALIAS = "wc_keystore_key" - -// When called more that once, different `storagePrefix` must be defined. -@JvmSynthetic -internal fun overrideModule( - relay: RelayConnectionInterface, - pairing: PairingInterface, - pairingController: PairingControllerInterface, - storagePrefix: String, - relayUrl: String, - connectionType: ConnectionType, - bundleId: String -) = module { - val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE - val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS - - single { relay } - - includes( - coreStorageModule(storagePrefix, bundleId), - corePairingModule(pairing, pairingController), - coreCryptoModule(sharedPrefsFile, keyStoreAlias), - coreJsonRpcModule(), - coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId) - ) -} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/activity/InstrumentedTestActivity.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/activity/InstrumentedTestActivity.kt deleted file mode 100644 index cd4af3754..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/activity/InstrumentedTestActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.notify.test.activity - -import android.app.Activity -import android.os.Bundle - -class InstrumentedTestActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } -} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/Common.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/Common.kt deleted file mode 100644 index 207b6e169..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/Common.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.walletconnect.notify.test.utils - -import com.walletconnect.android.Core -import com.walletconnect.notify.client.Notify -import junit.framework.TestCase.fail -import timber.log.Timber - -internal fun globalOnError(error: Notify.Model.Error) { - Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") - fail(error.throwable.message) -} - -internal fun globalOnError(error: Core.Model.Error) { - Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") - fail(error.throwable.message) -} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/TestClient.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/TestClient.kt deleted file mode 100644 index 0a43aa206..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/TestClient.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.walletconnect.notify.test.utils - -import android.app.Application -import androidx.test.core.app.ApplicationProvider -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.CoreProtocol -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayClient -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.notify.client.NotifyProtocol -import com.walletconnect.notify.client.cacao.CacaoSigner -import com.walletconnect.notify.di.overrideModule -import com.walletconnect.notify.test.BuildConfig -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import org.koin.core.KoinApplication -import timber.log.Timber - -internal object TestClient { - private val app = ApplicationProvider.getApplicationContext() - fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = init().apply { createEagerInstances() } - - internal const val account = "0x29163BFD38865971EfA5896e63bd6b7C8370E557" - internal const val caip10account = "eip155:1:$account" - internal const val publicKey = "5112cfe94b391100464b51af6f6714b4d7c21d25c92360b20b07278e722e033d426d26779f413330ceaf7cec2d5b4db91fe11bbb222955272f48478346549fe4" - internal val privateKey = PrivateKey("7191ca005ee3696cf1f9d1f35d3baabaee55188db12fca85f662af7cc181f896") - - object Primary { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Notify Primary", - description = "Notify Primary Client for automation tests", - url = "kotlin.e2e.notify.primary", - icons = listOf(), - redirect = null - ) - - private val coreProtocol = CoreClient.apply { - Timber.d("Primary CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) - Relay.connect(::globalOnError) - } - - private val initParams = Notify.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - - internal val notifyClient = - NotifyClient - .apply { - initialize(initParams, onError = { Timber.e(it.throwable) }) - }.also { notifyClient -> - - val isRegistered = notifyClient.isRegistered(params = Notify.Params.IsRegistered(caip10account, metadata.url)) - - if (!isRegistered) { - notifyClient.prepareRegistration(Notify.Params.PrepareRegistration(caip10account, metadata.url), - onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> - Timber.d("PrepareRegistration Success") - - val signature = CacaoSigner.sign(message, privateKey.keyAsBytes, SignatureType.EIP191) - notifyClient.register( - params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), - onSuccess = { identityKey -> - Timber.d("Primary CP finish: $identityKey") - _isInitialized.tryEmit(true) - }, - onError = { Timber.e(it.throwable.stackTraceToString()) } - ) - - }, - onError = { error -> - Timber.e(error.throwable) - throw error.throwable - }) - } - } - - internal val Relay get() = coreProtocol.Relay - } - - object Secondary { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Notify Secondary", - description = "Notify Secondary Client for automation tests", - url = "kotlin.e2e.notify.secondary", - icons = listOf(), - redirect = null - ) - - private val secondaryKoinApp = KoinApplication.createNewWCKoinApp() - - private val coreProtocol = CoreProtocol(secondaryKoinApp).apply { - Timber.d("Secondary CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } - - // Override of previous Relay necessary for reinitialization of `eventsFlow` - Relay = RelayClient(secondaryKoinApp) - - // Override of storage instances and depending objects - secondaryKoinApp.modules( - overrideModule( - Relay, - Pairing, - PairingController, - "test_secondary", - "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", - ConnectionType.MANUAL, - app.packageName - ) - ) - - // Necessary reinit of Relay, Pairing and PairingController - Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } - Pairing.initialize() - PairingController.initialize() - - Relay.connect(::globalOnError) - } - - private val initParams = Notify.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - - internal val notifyClient = NotifyProtocol(secondaryKoinApp).apply { - initialize(initParams, onError = { Timber.e(it.throwable) }) - }.also { notifyClient -> - val isRegistered = notifyClient.isRegistered(params = Notify.Params.IsRegistered(caip10account, metadata.url)) - - - if (!isRegistered) { - notifyClient.prepareRegistration(Notify.Params.PrepareRegistration(caip10account, metadata.url), - onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> - Timber.d("PrepareRegistration Success") - - val signature = CacaoSigner.sign(message, privateKey.keyAsBytes, SignatureType.EIP191) - - notifyClient.register( - params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), - onSuccess = { identityKey -> - Timber.d("Secondary CP finish: $identityKey") - _isInitialized.tryEmit(true) - }, - onError = { Timber.e(it.throwable.stackTraceToString()) } - ) - - }, - onError = { error -> - Timber.e(error.throwable) - throw error.throwable - }) - } - } - - internal val Relay get() = coreProtocol.Relay - } -} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyClient.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyClient.kt deleted file mode 100644 index 1b3b0f024..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyClient.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.notify.test.utils.primary - -import com.walletconnect.notify.test.utils.TestClient - -val PrimaryNotifyClient = TestClient.Primary.notifyClient diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyDelegate.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyDelegate.kt deleted file mode 100644 index b8fe40d30..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/primary/PrimaryNotifyDelegate.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.notify.test.utils.primary - -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyInterface -import com.walletconnect.notify.test.utils.globalOnError - -open class PrimaryNotifyDelegate : NotifyInterface.Delegate { - - override fun onNotifyNotification(notifyNotification: Notify.Event.Notification) { - } - - override fun onSubscriptionsChanged(subscriptionsChanged: Notify.Event.SubscriptionsChanged) { - } - - override fun onError(error: Notify.Model.Error) { - globalOnError(error) - } -} \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyClient.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyClient.kt deleted file mode 100644 index 882bc59ea..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyClient.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.notify.test.utils.secondary - -import com.walletconnect.notify.test.utils.TestClient - -val SecondaryNotifyClient = TestClient.Secondary.notifyClient \ No newline at end of file diff --git a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyDelegate.kt b/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyDelegate.kt deleted file mode 100644 index e7faaa829..000000000 --- a/protocol/notify/src/androidTest/kotlin/com/walletconnect/notify/test/utils/secondary/SecondaryNotifyDelegate.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.notify.test.utils.secondary - -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyInterface -import com.walletconnect.notify.test.utils.globalOnError - -open class SecondaryNotifyDelegate : NotifyInterface.Delegate { - - override fun onNotifyNotification(notifyNotification: Notify.Event.Notification) { - } - - override fun onSubscriptionsChanged(subscriptionsChanged: Notify.Event.SubscriptionsChanged) { - } - - override fun onError(error: Notify.Model.Error) { - globalOnError(error) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/AndroidManifest.xml b/protocol/notify/src/main/AndroidManifest.xml index 860f25157..eac37efdd 100644 --- a/protocol/notify/src/main/AndroidManifest.xml +++ b/protocol/notify/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.reown.notify"> \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/Notify.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/Notify.kt similarity index 95% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/client/Notify.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/client/Notify.kt index 156b014fa..d26905de2 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/Notify.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/Notify.kt @@ -1,11 +1,11 @@ -package com.walletconnect.notify.client +package com.reown.notify.client import android.net.Uri import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.foundation.common.model.PrivateKey +import com.reown.android.Core +import com.reown.android.CoreInterface +import com.reown.android.cacao.SignatureInterface +import com.reown.foundation.common.model.PrivateKey import kotlin.time.Duration object Notify { diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyClient.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyClient.kt new file mode 100644 index 000000000..3f31e37e6 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyClient.kt @@ -0,0 +1,5 @@ +package com.reown.notify.client + +object NotifyClient: NotifyInterface by NotifyProtocol.instance { + interface Delegate: NotifyInterface.Delegate +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyException.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyException.kt similarity index 86% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyException.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyException.kt index edd3b3aa4..59e0e1d24 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyException.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyException.kt @@ -1,6 +1,6 @@ -package com.walletconnect.notify.client +package com.reown.notify.client -import com.walletconnect.android.internal.common.exception.WalletConnectException +import com.reown.android.internal.common.exception.WalletConnectException class InvalidDidJsonFileException(override val message: String?) : WalletConnectException(message) class AccountIsNotRegisteredException(val account: String) : WalletConnectException("Account: $account is not registered") diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyInterface.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyInterface.kt similarity index 98% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyInterface.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyInterface.kt index 9645f096e..d571d6e33 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyInterface.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyInterface.kt @@ -1,4 +1,4 @@ -package com.walletconnect.notify.client +package com.reown.notify.client interface NotifyInterface { interface Delegate { diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyProtocol.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyProtocol.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyProtocol.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyProtocol.kt index 469fef6de..3f2de9a1d 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyProtocol.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/NotifyProtocol.kt @@ -1,18 +1,18 @@ -package com.walletconnect.notify.client - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.di.DatabaseConfig -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.notify.common.model.Notification -import com.walletconnect.notify.common.model.SubscriptionChanged -import com.walletconnect.notify.common.model.toClient -import com.walletconnect.notify.common.model.toCommon -import com.walletconnect.notify.di.engineModule -import com.walletconnect.notify.di.notifyJsonRpcModule -import com.walletconnect.notify.di.notifyStorageModule -import com.walletconnect.notify.engine.NotifyEngine +package com.reown.notify.client + +import com.reown.android.Core +import com.reown.android.internal.common.di.DatabaseConfig +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.notify.common.model.Notification +import com.reown.notify.common.model.SubscriptionChanged +import com.reown.notify.common.model.toClient +import com.reown.notify.common.model.toCommon +import com.reown.notify.di.engineModule +import com.reown.notify.di.notifyJsonRpcModule +import com.reown.notify.di.notifyStorageModule +import com.reown.notify.engine.NotifyEngine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/client/cacao/CacaoSigner.kt b/protocol/notify/src/main/kotlin/com/reown/notify/client/cacao/CacaoSigner.kt new file mode 100644 index 000000000..dcf89f4bd --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/client/cacao/CacaoSigner.kt @@ -0,0 +1,6 @@ +package com.reown.notify.client.cacao + +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.notify.client.Notify + +object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/JsonRpcMethod.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/JsonRpcMethod.kt new file mode 100644 index 000000000..8f6dc2c4a --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/JsonRpcMethod.kt @@ -0,0 +1,27 @@ +@file:JvmSynthetic + +package com.reown.notify.common + +internal object JsonRpcMethod { + + @get:JvmSynthetic + const val WC_NOTIFY_MESSAGE: String = "wc_notifyMessage" + + @get:JvmSynthetic + const val WC_NOTIFY_DELETE: String = "wc_notifyDelete" + + @get:JvmSynthetic + const val WC_NOTIFY_SUBSCRIBE: String = "wc_notifySubscribe" + + @get:JvmSynthetic + const val WC_NOTIFY_UPDATE: String = "wc_notifyUpdate" + + @get:JvmSynthetic + const val WC_NOTIFY_WATCH_SUBSCRIPTIONS: String = "wc_notifyWatchSubscriptions" + + @get:JvmSynthetic + const val WC_NOTIFY_SUBSCRIPTIONS_CHANGED: String = "wc_notifySubscriptionsChanged" + + @get:JvmSynthetic + const val WC_NOTIFY_GET_NOTIFICATIONS: String = "wc_notifyGetNotifications" +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/NotifyServerUrl.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/NotifyServerUrl.kt similarity index 85% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/NotifyServerUrl.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/NotifyServerUrl.kt index 0f500ade2..8e38d50d0 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/NotifyServerUrl.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/NotifyServerUrl.kt @@ -1,4 +1,4 @@ -package com.walletconnect.notify.common +package com.reown.notify.common import androidx.core.net.toUri diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/PeerError.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/PeerError.kt new file mode 100644 index 000000000..7d13b457d --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/PeerError.kt @@ -0,0 +1,36 @@ +@file:JvmSynthetic + +package com.reown.notify.common + +import com.reown.android.internal.common.model.type.Error + +// Documentation: https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/specs/clients/notify/error-codes.md +internal sealed class PeerError : Error { + + sealed class User: PeerError() { + data class Rejected(override val message: String) : User() { + override val code: Int = 5000 + } + + data class Unsubscribed(override val message: String) : User() { + override val code: Int = 6000 + } + + data class HasExistingSubscription(override val message: String) : User() { + override val code: Int = 6001 + } + } + + sealed class Failure: PeerError() { + + object ApprovalFailed: Failure() { + override val message: String = "Approval Failed" + override val code: Int = 7002 + } + + object RejectionFailed: Failure() { + override val message: String = "Rejection Failed" + override val code: Int = 7003 + } + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/UtilFunctions.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/UtilFunctions.kt new file mode 100644 index 000000000..5765fad03 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/UtilFunctions.kt @@ -0,0 +1,24 @@ +@file:JvmSynthetic + +package com.reown.notify.common + +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.utils.monthInSeconds +import java.nio.charset.Charset +import java.util.concurrent.TimeUnit + +@JvmSynthetic +internal fun calcExpiry(): Expiry { + val currentTimeMs = System.currentTimeMillis() + val currentTimeSeconds = TimeUnit.SECONDS.convert(currentTimeMs, TimeUnit.MILLISECONDS) + val expiryTimeSeconds = currentTimeSeconds + monthInSeconds + + return Expiry(expiryTimeSeconds) +} + + +@JvmSynthetic +internal fun convertToUTF8(input: String): String { + val bytes = input.toByteArray(Charset.forName("ISO-8859-1")) + return String(bytes, Charset.forName("UTF-8")) +} diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt new file mode 100644 index 000000000..86d8fb607 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt @@ -0,0 +1,6 @@ +package com.reown.notify.common.model + +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.foundation.common.model.PrivateKey + +internal data class CacaoPayloadWithIdentityPrivateKey(val payload: Cacao.Payload, val key: PrivateKey) diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CreateSubscription.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CreateSubscription.kt new file mode 100644 index 000000000..17fdc83b8 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/CreateSubscription.kt @@ -0,0 +1,14 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal sealed class CreateSubscription : EngineEvent { + + data class Success(val subscription: Subscription.Active) : CreateSubscription() + + data class Error(val throwable: Throwable) : CreateSubscription() + + object Processing : CreateSubscription() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/DeleteSubscription.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/DeleteSubscription.kt new file mode 100644 index 000000000..1f88879ca --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/DeleteSubscription.kt @@ -0,0 +1,14 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal sealed class DeleteSubscription : EngineEvent { + + data class Success(val topic: String) : DeleteSubscription() + + data class Error(val throwable: Throwable) : DeleteSubscription() + + object Processing : DeleteSubscription() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/EngineMapper.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/EngineMapper.kt new file mode 100644 index 000000000..a33a872ae --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/EngineMapper.kt @@ -0,0 +1,147 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.Core +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.utils.toClient +import com.reown.notify.client.Notify + +@JvmSynthetic +internal fun Core.Model.Message.Notify.toClient(topic: String): Notify.Model.Notification.Decrypted { + return Notify.Model.Notification.Decrypted(title, body, url, type, topic) +} + +@JvmSynthetic +@Throws(IllegalArgumentException::class) +internal fun Notification.toClient(): Notify.Model.NotificationRecord { + return Notify.Model.NotificationRecord( + id = this.id, + topic = this.topic, + sentAt = this.sentAt, + notification = Notify.Model.Notification.Decrypted( + title = this.notificationMessage.title, + body = this.notificationMessage.body, + url = this.notificationMessage.url, + type = this.notificationMessage.type, + topic = this.topic + ), + metadata = this.metadata?.let { it.toClient() } ?: run { throw IllegalArgumentException("Metadata is null") } + ) +} + + +@JvmSynthetic +internal fun Notification.toCore(): Core.Model.Message.Notify { + return Core.Model.Message.Notify( + title = this.notificationMessage.title, + body = this.notificationMessage.body, + url = this.notificationMessage.url, + type = this.notificationMessage.type, + topic = this.topic + ) +} + + +@JvmSynthetic +internal fun NotificationType.toClient(): Notify.Model.NotificationType { + return Notify.Model.NotificationType(id, name, description, iconUrl) +} + + +@JvmSynthetic +internal fun CacaoPayloadWithIdentityPrivateKey.toClient(): Notify.Model.CacaoPayloadWithIdentityPrivateKey { + return Notify.Model.CacaoPayloadWithIdentityPrivateKey(payload.toClient(), key) +} + + +@JvmSynthetic +internal fun Notify.Model.CacaoPayloadWithIdentityPrivateKey.toCommon(): CacaoPayloadWithIdentityPrivateKey { + return CacaoPayloadWithIdentityPrivateKey(payload.toCommon(), key) +} + + +@JvmSynthetic +internal fun Cacao.Payload.toClient(): Notify.Model.Cacao.Payload { + return Notify.Model.Cacao.Payload(iss, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) +} + + +@JvmSynthetic +internal fun Notify.Model.Cacao.Payload.toCommon(): Cacao.Payload { + return Cacao.Payload(iss, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) +} + + +@JvmSynthetic +internal fun Notify.Model.Cacao.Signature.toCommon(): Cacao.Signature = Cacao.Signature(t, s, m) + + +@JvmSynthetic +internal fun DeleteSubscription.toClient(): Notify.Result.DeleteSubscription = when (this) { + is DeleteSubscription.Success -> Notify.Result.DeleteSubscription.Success(topic) + is DeleteSubscription.Error -> Notify.Result.DeleteSubscription.Error(Notify.Model.Error(throwable)) + DeleteSubscription.Processing -> Notify.Result.DeleteSubscription.Error(Notify.Model.Error(IllegalStateException())) +} + +@JvmSynthetic +internal fun GetNotificationHistory.toClient(): Notify.Result.GetNotificationHistory = when (this) { + is GetNotificationHistory.Success -> Notify.Result.GetNotificationHistory.Success(notifications.toClient(), hasMore) + is GetNotificationHistory.Error -> Notify.Result.GetNotificationHistory.Error(Notify.Model.Error(throwable)) + GetNotificationHistory.Processing -> Notify.Result.GetNotificationHistory.Error(Notify.Model.Error(IllegalStateException())) +} + +@JvmSynthetic +internal fun List.toClient(): List { + return map { it.toClient() } +} + +@JvmSynthetic +internal fun SubscriptionChanged.toClient(): Notify.Event.SubscriptionsChanged = + Notify.Event.SubscriptionsChanged(subscriptions.map { it.toClient() }) + +@JvmSynthetic +internal fun CreateSubscription.toClient(): Notify.Result.Subscribe = when (this) { + is CreateSubscription.Success -> Notify.Result.Subscribe.Success(subscription.toClient()) + is CreateSubscription.Error -> Notify.Result.Subscribe.Error(Notify.Model.Error(throwable)) + CreateSubscription.Processing -> Notify.Result.Subscribe.Error(Notify.Model.Error(IllegalStateException())) +} + +@JvmSynthetic +internal fun Subscription.Active.toClient(): Notify.Model.Subscription { + return Notify.Model.Subscription( + topic = topic.value, + account = account.value, + relay = relay.toClient(), + metadata = dappMetaData.toClient(), + scope = mapOfScope.toClient(), + expiry = expiry.seconds, + ) +} + +@JvmSynthetic +internal fun UpdateSubscription.toClient(): Notify.Result.UpdateSubscription = when (this) { + is UpdateSubscription.Success -> Notify.Result.UpdateSubscription.Success(subscription.toClient()) + is UpdateSubscription.Error -> Notify.Result.UpdateSubscription.Error(Notify.Model.Error(IllegalStateException())) + UpdateSubscription.Processing -> Notify.Result.UpdateSubscription.Error(Notify.Model.Error(IllegalStateException())) + +} + +@JvmSynthetic +internal fun RelayProtocolOptions.toClient(): Notify.Model.Subscription.Relay { + return Notify.Model.Subscription.Relay(protocol, data) +} + +@JvmSynthetic +internal fun Map.toClient(): Map { + return map { (key, value) -> + Notify.Model.Subscription.ScopeId(key) to Notify.Model.Subscription.ScopeSetting(value.name, value.description, value.isSelected) + }.toMap() +} + +@JvmSynthetic +internal fun SDKError.toClient(): Notify.Model.Error { + return Notify.Model.Error(exception) +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Error.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Error.kt new file mode 100644 index 000000000..12f025908 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Error.kt @@ -0,0 +1,10 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal data class Error( + val requestId: Long, + val rejectionReason: String, +) : EngineEvent \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/GetNotificationHistory.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/GetNotificationHistory.kt new file mode 100644 index 000000000..9c932b98e --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/GetNotificationHistory.kt @@ -0,0 +1,14 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal sealed class GetNotificationHistory : EngineEvent { + + data class Success(val notifications: List, val hasMore: Boolean) : GetNotificationHistory() + + data class Error(val throwable: Throwable) : GetNotificationHistory() + + object Processing : GetNotificationHistory() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Notification.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Notification.kt new file mode 100644 index 000000000..8a81a29f5 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Notification.kt @@ -0,0 +1,14 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.type.EngineEvent + +internal data class Notification( + val id: String, + val topic: String, + val sentAt: Long, + val notificationMessage: NotificationMessage, + val metadata: AppMetaData?, +) : EngineEvent \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationMessage.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationMessage.kt similarity index 79% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationMessage.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationMessage.kt index be2b52a7a..a93b9a249 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationMessage.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationMessage.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.notify.common.model +package com.reown.notify.common.model internal data class NotificationMessage( val title: String, diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationType.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationType.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationType.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationType.kt index 3be6677f3..0bc637e46 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotificationType.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotificationType.kt @@ -1,3 +1,3 @@ -package com.walletconnect.notify.common.model +package com.reown.notify.common.model internal data class NotificationType(val id: String, val name: String, val description: String, val isEnabled: Boolean, val iconUrl: String?) \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotifyRpc.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotifyRpc.kt similarity index 91% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotifyRpc.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotifyRpc.kt index f9c7380c0..5facc4952 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/NotifyRpc.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/NotifyRpc.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.notify.common.model +package com.reown.notify.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.notify.common.JsonRpcMethod -import com.walletconnect.util.generateId +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.notify.common.JsonRpcMethod +import com.reown.util.generateId internal sealed class NotifyRpc : JsonRpcClientSync { diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/RegisteredAccount.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/RegisteredAccount.kt new file mode 100644 index 000000000..c312c2b11 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/RegisteredAccount.kt @@ -0,0 +1,14 @@ +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic + +data class RegisteredAccount( + val accountId: AccountId, + val publicIdentityKey: PublicKey, + val allApps: Boolean, + val appDomain: String?, + val notifyServerWatchTopic: Topic?, + val notifyServerAuthenticationKey: PublicKey?, +) \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Scope.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Scope.kt new file mode 100644 index 000000000..ae63a210b --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Scope.kt @@ -0,0 +1,23 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +internal sealed class Scope { + abstract val id: String + abstract val name: String + abstract val description: String + + data class Remote( + override val id: String, + override val name: String, + override val description: String, + val iconUrl: String?, + ) : Scope() + + data class Cached( + override val id: String, + override val name: String, + override val description: String, + val isSelected: Boolean, + ) : Scope() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerNotification.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerNotification.kt similarity index 87% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerNotification.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerNotification.kt index 7a0fabeed..0573e8f67 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerNotification.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerNotification.kt @@ -1,4 +1,4 @@ -package com.walletconnect.notify.common.model +package com.reown.notify.common.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerSubscription.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerSubscription.kt similarity index 91% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerSubscription.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerSubscription.kt index 24a7f6232..378ee2554 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/ServerSubscription.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/ServerSubscription.kt @@ -1,4 +1,4 @@ -package com.walletconnect.notify.common.model +package com.reown.notify.common.model import com.squareup.moshi.JsonClass diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Subscription.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Subscription.kt new file mode 100644 index 000000000..55608e776 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/Subscription.kt @@ -0,0 +1,29 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic + +internal sealed class Subscription { + abstract val account: AccountId + abstract val mapOfScope: Map + abstract val expiry: Expiry + + data class Active( + override val account: AccountId, + override val mapOfScope: Map, + override val expiry: Expiry, + val authenticationPublicKey: PublicKey, + val topic: Topic, + val dappMetaData: AppMetaData? = null, + val requestedSubscriptionId: Long? = null, + val relay: RelayProtocolOptions = RelayProtocolOptions(), + val lastNotificationId: String? = null, + val reachedEndOfHistory: Boolean = false, + ) : Subscription() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/SubscriptionChanged.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/SubscriptionChanged.kt new file mode 100644 index 000000000..4de92cea9 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/SubscriptionChanged.kt @@ -0,0 +1,7 @@ +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal data class SubscriptionChanged( + val subscriptions: List, +) : EngineEvent diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/TimeoutInfo.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/TimeoutInfo.kt new file mode 100644 index 000000000..757c5bb33 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/TimeoutInfo.kt @@ -0,0 +1,8 @@ +package com.reown.notify.common.model + +import com.reown.foundation.common.model.Topic + +internal sealed interface TimeoutInfo { + data class Data(val requestId: Long, val topic: Topic) : TimeoutInfo + object Nothing : TimeoutInfo +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/common/model/UpdateSubscription.kt b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/UpdateSubscription.kt new file mode 100644 index 000000000..16fcdb83b --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/common/model/UpdateSubscription.kt @@ -0,0 +1,14 @@ +@file:JvmSynthetic + +package com.reown.notify.common.model + +import com.reown.android.internal.common.model.type.EngineEvent + +internal sealed class UpdateSubscription : EngineEvent { + + data class Success(val subscription: Subscription.Active) : UpdateSubscription() + + data class Error(val throwable: Throwable) : UpdateSubscription() + + object Processing : UpdateSubscription() +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/NotifyJwtBase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/NotifyJwtBase.kt similarity index 94% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/NotifyJwtBase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/NotifyJwtBase.kt index cbb3a9898..5312dd410 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/NotifyJwtBase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/NotifyJwtBase.kt @@ -1,8 +1,8 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt +package com.reown.notify.data.jwt -import com.walletconnect.foundation.util.jwt.JwtClaims +import com.reown.foundation.util.jwt.JwtClaims import java.util.concurrent.TimeUnit internal interface NotifyJwtBase : JwtClaims { diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteRequestJwtClaim.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteRequestJwtClaim.kt index 47629d83b..1f5c0fe2e 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteRequestJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.delete +package com.reown.notify.data.jwt.delete import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class DeleteRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteResponseJwtClaim.kt similarity index 84% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteResponseJwtClaim.kt index fc8ade40a..cea33368e 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/DeleteResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/DeleteResponseJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.delete +package com.reown.notify.data.jwt.delete import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class DeleteResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt new file mode 100644 index 000000000..c87ae58e7 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt @@ -0,0 +1,29 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.delete + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeDeleteRequestJwtUseCase( + private val app: String, + private val accountId: AccountId, + private val authenticationKey: PublicKey, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): DeleteRequestJwtClaim = with(params) { + DeleteRequestJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + app = encodeDidWeb(app) + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt new file mode 100644 index 000000000..094975598 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt @@ -0,0 +1,33 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.getNotifications + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeGetNotificationsRequestJwtUseCase( + private val app: String, + private val accountId: AccountId, + private val authenticationKey: PublicKey, + private val limit: Int, + private val after: String?, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): GetNotificationsRequestJwtClaim = with(params) { + GetNotificationsRequestJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + app = encodeDidWeb(app), + limit = limit, + after = after, + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt index 659d533b7..0b0bcde32 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsRequestJwtClaim.kt @@ -1,8 +1,8 @@ -package com.walletconnect.notify.data.jwt.getNotifications +package com.reown.notify.data.jwt.getNotifications import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt similarity index 83% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt index cdc137e84..863d47aeb 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/getNotifications/GetNotificationsResponseJwtClaim.kt @@ -1,9 +1,9 @@ -package com.walletconnect.notify.data.jwt.getNotifications +package com.reown.notify.data.jwt.getNotifications import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerNotification -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerNotification +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class GetNotificationsResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt new file mode 100644 index 000000000..ff354779c --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt @@ -0,0 +1,29 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.message + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeMessageResponseJwtUseCase( + private val app: String, + private val accountId: AccountId, + private val authenticationKey: PublicKey, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): MessageResponseJwtClaim = with(params) { + MessageResponseJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + app = encodeDidWeb(app) + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageRequestJwtClaim.kt similarity index 83% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageRequestJwtClaim.kt index a0faeb079..db4f0a346 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageRequestJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.message +package com.reown.notify.data.jwt.message import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerNotification -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerNotification +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class MessageRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageResponseJwtClaim.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageResponseJwtClaim.kt index 9ed5f8849..19ed2c906 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/MessageResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/message/MessageResponseJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.message +package com.reown.notify.data.jwt.message import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class MessageResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt new file mode 100644 index 000000000..dbfde2f0b --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt @@ -0,0 +1,31 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.subscription + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeSubscriptionRequestJwtUseCase( + private val app: String, + private val accountId: AccountId, + private val authenticationKey: PublicKey, + private val scope: String +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): SubscriptionRequestJwtClaim = with(params) { + SubscriptionRequestJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + scope = scope, + app = encodeDidWeb(app) + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt index bb789716b..042b736f7 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionRequestJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.subscription +package com.reown.notify.data.jwt.subscription import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class SubscriptionRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt similarity index 84% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt index ddb038fe9..66f827174 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscription/SubscriptionResponseJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.subscription +package com.reown.notify.data.jwt.subscription import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class SubscriptionResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt new file mode 100644 index 000000000..8974548f0 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt @@ -0,0 +1,26 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.subscriptionsChanged + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeSubscriptionsChangedResponseJwtUseCase( + private val accountId: AccountId, + private val authenticationKey: PublicKey, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): SubscriptionsChangedResponseJwtClaim = with(params) { + SubscriptionsChangedResponseJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt similarity index 82% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt index d702e1f52..8ded7a8fb 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedRequestJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.subscriptionsChanged +package com.reown.notify.data.jwt.subscriptionsChanged import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class SubscriptionsChangedRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt similarity index 88% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt index dbdc1bb32..e11e29678 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/subscriptionsChanged/SubscriptionsChangedResponseJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.subscriptionsChanged +package com.reown.notify.data.jwt.subscriptionsChanged import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class SubscriptionsChangedResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt new file mode 100644 index 000000000..fe8a698a4 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt @@ -0,0 +1,31 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.update + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeUpdateRequestJwtUseCase( + private val accountId: AccountId, + private val app: String, + private val authenticationKey: PublicKey, + private val scope: String, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): UpdateRequestJwtClaim = with(params) { + UpdateRequestJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + app = encodeDidWeb(app), + scope = scope, + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateRequestJwtClaim.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateRequestJwtClaim.kt index 26bb239a7..d172dc1c7 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateRequestJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.update +package com.reown.notify.data.jwt.update import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class UpdateRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateResponseJwtClaim.kt similarity index 84% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateResponseJwtClaim.kt index ac9c05463..543e1f273 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/UpdateResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/update/UpdateResponseJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.update +package com.reown.notify.data.jwt.update import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class UpdateResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt new file mode 100644 index 000000000..f1fbfd338 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt @@ -0,0 +1,29 @@ +@file:JvmSynthetic + +package com.reown.notify.data.jwt.watchSubscriptions + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.jwt.encodeDidPkh +import com.reown.foundation.util.jwt.encodeDidWeb +import com.reown.foundation.util.jwt.encodeEd25519DidKey + +internal class EncodeWatchSubscriptionsRequestJwtUseCase( + private val accountId: AccountId, + private val authenticationKey: PublicKey, + private val appDomain: String?, +) : EncodeDidJwtPayloadUseCase { + + override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): WatchSubscriptionsRequestJwtClaim = with(params) { + WatchSubscriptionsRequestJwtClaim( + issuedAt = issuedAt, + expiration = expiration, + issuer = issuer, + keyserverUrl = keyserverUrl, + audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), + subject = encodeDidPkh(accountId.value), + appDidWeb = appDomain?.let { encodeDidWeb(it) } + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt similarity index 88% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt index 1ae540b55..88d614065 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsRequestJwtClaim.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.watchSubscriptions +package com.reown.notify.data.jwt.watchSubscriptions import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class WatchSubscriptionsRequestJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt similarity index 83% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt index b087d2e64..f0879e9d2 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/jwt/watchSubscriptions/WatchSubscriptionsResponseJwtClaim.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.jwt.watchSubscriptions +package com.reown.notify.data.jwt.watchSubscriptions import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.data.jwt.NotifyJwtBase +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.data.jwt.NotifyJwtBase @JsonClass(generateAdapter = true) internal data class WatchSubscriptionsResponseJwtClaim( diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/NotificationsRepository.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/NotificationsRepository.kt similarity index 89% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/NotificationsRepository.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/storage/NotificationsRepository.kt index 6abe012be..5409e8c5f 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/NotificationsRepository.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/NotificationsRepository.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.storage +package com.reown.notify.data.storage -import com.walletconnect.notify.common.model.Notification -import com.walletconnect.notify.common.model.NotificationMessage -import com.walletconnect.notify.common.storage.data.dao.NotificationsQueries +import com.reown.notify.common.model.Notification +import com.reown.notify.common.model.NotificationMessage +import com.reown.notify.common.storage.data.dao.NotificationsQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/RegisteredAccountsRepository.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/RegisteredAccountsRepository.kt similarity index 84% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/RegisteredAccountsRepository.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/storage/RegisteredAccountsRepository.kt index 689e0558b..c2ae6b2cf 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/RegisteredAccountsRepository.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/RegisteredAccountsRepository.kt @@ -1,12 +1,12 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.storage +package com.reown.notify.data.storage -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.notify.common.model.RegisteredAccount -import com.walletconnect.notify.common.storage.data.dao.RegisteredAccountsQueries +import com.reown.android.internal.common.model.AccountId +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.notify.common.model.RegisteredAccount +import com.reown.notify.common.storage.data.dao.RegisteredAccountsQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/SubscriptionRepository.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/SubscriptionRepository.kt similarity index 88% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/SubscriptionRepository.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/storage/SubscriptionRepository.kt index cedb521b9..9166161c2 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/storage/SubscriptionRepository.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/storage/SubscriptionRepository.kt @@ -1,15 +1,15 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.storage +package com.reown.notify.data.storage -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.notify.common.model.Scope -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.common.storage.data.dao.ActiveSubscriptionsQueries +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.notify.common.model.Scope +import com.reown.notify.common.model.Subscription +import com.reown.notify.common.storage.data.dao.ActiveSubscriptionsQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/DidJsonDTO.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/DidJsonDTO.kt similarity index 90% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/DidJsonDTO.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/DidJsonDTO.kt index fbc7a9730..76bc2cf08 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/DidJsonDTO.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/DidJsonDTO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.wellknown.did +package com.reown.notify.data.wellknown.did import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/PublicKeyJwkDTO.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/PublicKeyJwkDTO.kt similarity index 84% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/PublicKeyJwkDTO.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/PublicKeyJwkDTO.kt index dc30cdab8..a54d86e6a 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/PublicKeyJwkDTO.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/PublicKeyJwkDTO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.wellknown.did +package com.reown.notify.data.wellknown.did import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/VerificationMethodDTO.kt b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/VerificationMethodDTO.kt similarity index 88% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/VerificationMethodDTO.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/VerificationMethodDTO.kt index 17b138fa3..64c0629b5 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/wellknown/did/VerificationMethodDTO.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/data/wellknown/did/VerificationMethodDTO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.notify.data.wellknown.did +package com.reown.notify.data.wellknown.did import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/di/CallModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/CallModule.kt new file mode 100644 index 000000000..ee3deb07d --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/CallModule.kt @@ -0,0 +1,146 @@ +@file:JvmSynthetic + +package com.reown.notify.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.model.Tags +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.notify.engine.calls.DecryptNotifyMessageUseCase +import com.reown.notify.engine.calls.DeleteSubscriptionUseCase +import com.reown.notify.engine.calls.DeleteSubscriptionUseCaseInterface +import com.reown.notify.engine.calls.GetActiveSubscriptionsUseCase +import com.reown.notify.engine.calls.GetActiveSubscriptionsUseCaseInterface +import com.reown.notify.engine.calls.GetNotificationHistoryUseCase +import com.reown.notify.engine.calls.GetNotificationHistoryUseCaseInterface +import com.reown.notify.engine.calls.GetNotificationTypesUseCase +import com.reown.notify.engine.calls.GetNotificationTypesUseCaseInterface +import com.reown.notify.engine.calls.IsRegisteredUseCase +import com.reown.notify.engine.calls.IsRegisteredUseCaseInterface +import com.reown.notify.engine.calls.PrepareRegistrationUseCase +import com.reown.notify.engine.calls.PrepareRegistrationUseCaseInterface +import com.reown.notify.engine.calls.RegisterUseCase +import com.reown.notify.engine.calls.RegisterUseCaseInterface +import com.reown.notify.engine.calls.SubscribeToDappUseCase +import com.reown.notify.engine.calls.SubscribeToDappUseCaseInterface +import com.reown.notify.engine.calls.UnregisterUseCase +import com.reown.notify.engine.calls.UnregisterUseCaseInterface +import com.reown.notify.engine.calls.UpdateSubscriptionUseCase +import com.reown.notify.engine.calls.UpdateSubscriptionUseCaseInterface +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@JvmSynthetic +internal fun callModule() = module { + + single { + SubscribeToDappUseCase( + jsonRpcInteractor = get(), + crypto = get(), + extractMetadataFromConfigUseCase = get(), + metadataStorageRepository = get(), + fetchDidJwtInteractor = get(), + extractPublicKeysFromDidJson = get(), + onSubscribeResponseUseCase = get(), + subscriptionRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + UpdateSubscriptionUseCase( + jsonRpcInteractor = get(), + subscriptionRepository = get(), + metadataStorageRepository = get(), + fetchDidJwtInteractor = get(), + onUpdateResponseUseCase = get() + ) + } + + single { + DeleteSubscriptionUseCase( + jsonRpcInteractor = get(), + metadataStorageRepository = get(), + subscriptionRepository = get(), + fetchDidJwtInteractor = get(), + onDeleteResponseUseCase = get() + ) + } + + single(named(AndroidCommonDITags.DECRYPT_NOTIFY_MESSAGE)) { + val useCase = DecryptNotifyMessageUseCase( + codec = get(), + serializer = get(), + jsonRpcHistory = get(), + notificationsRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + metadataStorageRepository = get() + ) + + get>(named(AndroidCommonDITags.DECRYPT_USE_CASES))[Tags.NOTIFY_MESSAGE.id.toString()] = useCase + useCase + } + + single { + RegisterUseCase( + registeredAccountsRepository = get(), + identitiesInteractor = get(), + watchSubscriptionsUseCase = get(), + keyManagementRepository = get(), + projectId = get() + ) + } + + single { + IsRegisteredUseCase( + registeredAccountsRepository = get(), + identitiesInteractor = get(), + identityServerUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)) + ) + } + + single { + PrepareRegistrationUseCase( + identitiesInteractor = get(), + identityServerUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), + keyManagementRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + UnregisterUseCase( + registeredAccountsRepository = get(), + stopWatchingSubscriptionsUseCase = get(), + identitiesInteractor = get(), + keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), + subscriptionRepository = get(), + notificationsRepository = get(), + jsonRpcInteractor = get() + ) + } + + single { + GetNotificationTypesUseCase( + getNotifyConfigUseCase = get() + ) + } + + single { + GetActiveSubscriptionsUseCase( + subscriptionRepository = get(), + metadataStorageRepository = get() + ) + } + + single { + GetNotificationHistoryUseCase( + jsonRpcInteractor = get(), + subscriptionRepository = get(), + metadataStorageRepository = get(), + notificationsRepository = get(), + fetchDidJwtInteractor = get(), + onGetNotificationsResponseUseCase = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyEngineModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyEngineModule.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyEngineModule.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyEngineModule.kt index bf5a0091b..316983773 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyEngineModule.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyEngineModule.kt @@ -1,21 +1,21 @@ @file:JvmSynthetic -package com.walletconnect.notify.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.notify.common.NotifyServerUrl -import com.walletconnect.notify.engine.NotifyEngine -import com.walletconnect.notify.engine.calls.GetAllActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.domain.ExtractMetadataFromConfigUseCase -import com.walletconnect.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.domain.FindRequestedSubscriptionUseCase -import com.walletconnect.notify.engine.domain.GenerateAppropriateUriUseCase -import com.walletconnect.notify.engine.domain.GetSelfKeyForWatchSubscriptionUseCase -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.domain.StopWatchingSubscriptionsUseCase -import com.walletconnect.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase -import com.walletconnect.notify.engine.domain.WatchSubscriptionsUseCase +package com.reown.notify.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.notify.common.NotifyServerUrl +import com.reown.notify.engine.NotifyEngine +import com.reown.notify.engine.calls.GetAllActiveSubscriptionsUseCase +import com.reown.notify.engine.domain.ExtractMetadataFromConfigUseCase +import com.reown.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.domain.FindRequestedSubscriptionUseCase +import com.reown.notify.engine.domain.GenerateAppropriateUriUseCase +import com.reown.notify.engine.domain.GetSelfKeyForWatchSubscriptionUseCase +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import com.reown.notify.engine.domain.StopWatchingSubscriptionsUseCase +import com.reown.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase +import com.reown.notify.engine.domain.WatchSubscriptionsUseCase import org.koin.core.qualifier.named import org.koin.dsl.module diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyJsonRpcModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyJsonRpcModule.kt similarity index 83% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyJsonRpcModule.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyJsonRpcModule.kt index 66e5b200c..39d0e81e3 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyJsonRpcModule.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyJsonRpcModule.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.di +package com.reown.notify.di -import com.walletconnect.notify.common.JsonRpcMethod -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.utils.addDeserializerEntry -import com.walletconnect.utils.addSerializerEntry +import com.reown.notify.common.JsonRpcMethod +import com.reown.notify.common.model.NotifyRpc +import com.reown.utils.addDeserializerEntry +import com.reown.utils.addSerializerEntry import org.koin.dsl.module @JvmSynthetic diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyStorageModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyStorageModule.kt similarity index 86% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyStorageModule.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyStorageModule.kt index 431fb11c0..3749d5d96 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/NotifyStorageModule.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/NotifyStorageModule.kt @@ -1,15 +1,15 @@ @file:JvmSynthetic -package com.walletconnect.notify.di +package com.reown.notify.di import app.cash.sqldelight.ColumnAdapter -import com.walletconnect.android.di.sdkBaseStorageModule -import com.walletconnect.android.internal.common.di.deleteDatabase -import com.walletconnect.notify.NotifyDatabase -import com.walletconnect.notify.common.storage.data.dao.ActiveSubscriptions -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.data.storage.SubscriptionRepository +import com.reown.android.di.sdkBaseStorageModule +import com.reown.android.internal.common.di.deleteDatabase +import com.reown.notify.NotifyDatabase +import com.reown.notify.common.storage.data.dao.ActiveSubscriptions +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.data.storage.SubscriptionRepository import org.koin.core.qualifier.named import org.koin.core.scope.Scope import org.koin.dsl.module diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/RequestModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/RequestModule.kt similarity index 79% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/di/RequestModule.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/di/RequestModule.kt index d2a5c1ad4..c7aa13832 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/RequestModule.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/RequestModule.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.di +package com.reown.notify.di -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.notify.engine.requests.OnMessageUseCase -import com.walletconnect.notify.engine.requests.OnSubscriptionsChangedUseCase +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.notify.engine.requests.OnMessageUseCase +import com.reown.notify.engine.requests.OnSubscriptionsChangedUseCase import org.koin.core.qualifier.named import org.koin.dsl.module diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/ResponseModule.kt b/protocol/notify/src/main/kotlin/com/reown/notify/di/ResponseModule.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/di/ResponseModule.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/di/ResponseModule.kt index ca50eedcc..4a32a44b3 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/ResponseModule.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/di/ResponseModule.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.notify.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.notify.engine.responses.OnDeleteResponseUseCase -import com.walletconnect.notify.engine.responses.OnGetNotificationsResponseUseCase -import com.walletconnect.notify.engine.responses.OnSubscribeResponseUseCase -import com.walletconnect.notify.engine.responses.OnUpdateResponseUseCase -import com.walletconnect.notify.engine.responses.OnWatchSubscriptionsResponseUseCase +package com.reown.notify.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.notify.engine.responses.OnDeleteResponseUseCase +import com.reown.notify.engine.responses.OnGetNotificationsResponseUseCase +import com.reown.notify.engine.responses.OnSubscribeResponseUseCase +import com.reown.notify.engine.responses.OnUpdateResponseUseCase +import com.reown.notify.engine.responses.OnWatchSubscriptionsResponseUseCase import org.koin.core.qualifier.named import org.koin.dsl.module diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/NotifyEngine.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/NotifyEngine.kt similarity index 76% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/NotifyEngine.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/NotifyEngine.kt index ec22f9324..9e67b1919 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/NotifyEngine.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/NotifyEngine.kt @@ -1,43 +1,42 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine - -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.notify.common.JsonRpcMethod -import com.walletconnect.notify.engine.calls.DeleteSubscriptionUseCaseInterface -import com.walletconnect.notify.engine.calls.GetActiveSubscriptionsUseCaseInterface -import com.walletconnect.notify.engine.calls.GetAllActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.calls.GetNotificationHistoryUseCaseInterface -import com.walletconnect.notify.engine.calls.GetNotificationTypesUseCaseInterface -import com.walletconnect.notify.engine.calls.IsRegisteredUseCaseInterface -import com.walletconnect.notify.engine.calls.PrepareRegistrationUseCaseInterface -import com.walletconnect.notify.engine.calls.RegisterUseCaseInterface -import com.walletconnect.notify.engine.calls.SubscribeToDappUseCaseInterface -import com.walletconnect.notify.engine.calls.UnregisterUseCaseInterface -import com.walletconnect.notify.engine.calls.UpdateSubscriptionUseCaseInterface -import com.walletconnect.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase -import com.walletconnect.notify.engine.requests.OnMessageUseCase -import com.walletconnect.notify.engine.requests.OnSubscriptionsChangedUseCase -import com.walletconnect.notify.engine.responses.OnDeleteResponseUseCase -import com.walletconnect.notify.engine.responses.OnGetNotificationsResponseUseCase -import com.walletconnect.notify.engine.responses.OnSubscribeResponseUseCase -import com.walletconnect.notify.engine.responses.OnUpdateResponseUseCase -import com.walletconnect.notify.engine.responses.OnWatchSubscriptionsResponseUseCase +package com.reown.notify.engine + +import com.reown.android.internal.common.model.ConnectionState +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.android.relay.WSSConnectionState +import com.reown.notify.common.JsonRpcMethod +import com.reown.notify.engine.calls.DeleteSubscriptionUseCaseInterface +import com.reown.notify.engine.calls.GetActiveSubscriptionsUseCaseInterface +import com.reown.notify.engine.calls.GetAllActiveSubscriptionsUseCase +import com.reown.notify.engine.calls.GetNotificationHistoryUseCaseInterface +import com.reown.notify.engine.calls.GetNotificationTypesUseCaseInterface +import com.reown.notify.engine.calls.IsRegisteredUseCaseInterface +import com.reown.notify.engine.calls.PrepareRegistrationUseCaseInterface +import com.reown.notify.engine.calls.RegisterUseCaseInterface +import com.reown.notify.engine.calls.SubscribeToDappUseCaseInterface +import com.reown.notify.engine.calls.UnregisterUseCaseInterface +import com.reown.notify.engine.calls.UpdateSubscriptionUseCaseInterface +import com.reown.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase +import com.reown.notify.engine.requests.OnMessageUseCase +import com.reown.notify.engine.requests.OnSubscriptionsChangedUseCase +import com.reown.notify.engine.responses.OnDeleteResponseUseCase +import com.reown.notify.engine.responses.OnGetNotificationsResponseUseCase +import com.reown.notify.engine.responses.OnSubscribeResponseUseCase +import com.reown.notify.engine.responses.OnUpdateResponseUseCase +import com.reown.notify.engine.responses.OnWatchSubscriptionsResponseUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach @@ -101,9 +100,7 @@ internal class NotifyEngine( } suspend fun setup() { - jsonRpcInteractor.wssConnectionState - .onEach { state -> handleWSSState(state) } - .filterIsInstance() + jsonRpcInteractor.onResubscribe .onEach { supervisorScope { launch(Dispatchers.IO) { diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DecryptNotifyMessageUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DecryptNotifyMessageUseCase.kt new file mode 100644 index 000000000..1f84dee2b --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DecryptNotifyMessageUseCase.kt @@ -0,0 +1,73 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.Core +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.notify.common.model.Notification +import com.reown.notify.common.model.NotificationMessage +import com.reown.notify.common.model.toCore +import com.reown.notify.data.jwt.message.MessageRequestJwtClaim +import com.reown.notify.data.storage.NotificationsRepository +import kotlinx.coroutines.supervisorScope +import org.bouncycastle.util.encoders.Base64 +import kotlin.reflect.safeCast + +internal class DecryptNotifyMessageUseCase( + private val codec: Codec, + private val serializer: JsonRpcSerializer, + private val jsonRpcHistory: JsonRpcHistory, + private val notificationsRepository: NotificationsRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val logger: Logger +) : DecryptMessageUseCaseInterface { + + override suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + try { + val decryptedMessageString = codec.decrypt(Topic(topic), Base64.decode(message)) + val messageHash = sha256(decryptedMessageString.toByteArray()) + + if (messageHash !in jsonRpcHistory.getListOfPendingRecords().map { sha256(it.body.toByteArray()) }) { + val clientJsonRpc = serializer.tryDeserialize(decryptedMessageString) + ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match the Message format: $decryptedMessageString")) + val notifyMessageJwt = CoreNotifyParams.MessageParams::class.safeCast(serializer.deserialize(clientJsonRpc.method, decryptedMessageString)) + ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) + val messageRequestJwt = extractVerifiedDidJwtClaims(notifyMessageJwt.messageAuth).getOrElse { + return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) + } + + val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(Topic(topic), AppMetaDataType.PEER) + ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) + + with(messageRequestJwt.serverNotification) { + if (!notificationsRepository.doesNotificationsExistsByNotificationId(id)) { + + val notification = Notification( + id = id, topic = topic, sentAt = sentAt, metadata = metadata, notificationMessage = NotificationMessage(title = title, body = body, icon = icon, url = url, type = type) + ) + + notificationsRepository.insertOrReplaceNotification(notification) + onSuccess(notification.toCore()) + } else { + logger.log("DecryptNotifyMessageUseCase - notification already exists $id") + } + } + + } + } catch (e: Exception) { + onFailure(e) + } + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DeleteSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DeleteSubscriptionUseCase.kt new file mode 100644 index 000000000..aefce8e79 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/DeleteSubscriptionUseCase.kt @@ -0,0 +1,89 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.monthInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.notify.common.model.DeleteSubscription +import com.reown.notify.common.model.NotifyRpc +import com.reown.notify.common.model.Subscription +import com.reown.notify.common.model.TimeoutInfo +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.blockingCallsDefaultTimeout +import com.reown.notify.engine.blockingCallsDelayInterval +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.responses.OnDeleteResponseUseCase +import com.reown.notify.engine.validateTimeout +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeout +import kotlin.time.Duration + +internal class DeleteSubscriptionUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val subscriptionRepository: SubscriptionRepository, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val onDeleteResponseUseCase: OnDeleteResponseUseCase, +) : DeleteSubscriptionUseCaseInterface { + + override suspend fun deleteSubscription(topic: String, timeout: Duration?): DeleteSubscription = supervisorScope { + val result = MutableStateFlow(DeleteSubscription.Processing) + var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing + try { + val validTimeout = timeout.validateTimeout() + val activeSubscription: Subscription.Active = subscriptionRepository.getActiveSubscriptionByNotifyTopic(topic) + ?: throw IllegalStateException("Subscription does not exists for $topic") + val dappMetaData = metadataStorageRepository.getByTopicAndType(activeSubscription.topic, AppMetaDataType.PEER) + ?: throw IllegalStateException("Dapp metadata does not exists for $topic") + + val deleteJwt = fetchDidJwtInteractor.deleteRequest(activeSubscription.account, dappMetaData.url, activeSubscription.authenticationPublicKey).getOrThrow() + + val params = CoreNotifyParams.DeleteParams(deleteJwt.value) + val request = NotifyRpc.NotifyDelete(params = params) + val irnParams = IrnParams(Tags.NOTIFY_DELETE, Ttl(monthInSeconds)) + timeoutInfo = TimeoutInfo.Data(request.id, Topic(topic)) + + onDeleteResponseUseCase.events + .filter { it.first == params } + .map { it.second } + .filter { it is DeleteSubscription.Success || it is DeleteSubscription.Error } + .onEach { result.emit(it as DeleteSubscription) } + .launchIn(scope) + + jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, request, onFailure = { error -> result.value = DeleteSubscription.Error(error) }) + + withTimeout(validTimeout) { + while (result.value == DeleteSubscription.Processing) { + delay(blockingCallsDelayInterval) + } + } + + return@supervisorScope result.value + } catch (e: TimeoutCancellationException) { + with(timeoutInfo as TimeoutInfo.Data) { + return@supervisorScope DeleteSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) + } + } catch (e: Exception) { + return@supervisorScope DeleteSubscription.Error(e) + } + } +} + +internal interface DeleteSubscriptionUseCaseInterface { + suspend fun deleteSubscription(topic: String, timeout: Duration? = null): DeleteSubscription +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetActiveSubscriptionsUseCase.kt new file mode 100644 index 000000000..f0b89cd51 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetActiveSubscriptionsUseCase.kt @@ -0,0 +1,38 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.notify.common.model.Subscription +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.validateTimeout +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.withTimeout +import kotlin.time.Duration + +internal class GetActiveSubscriptionsUseCase( + private val subscriptionRepository: SubscriptionRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, +) : GetActiveSubscriptionsUseCaseInterface { + + @Throws(TimeoutCancellationException::class) + override suspend fun getActiveSubscriptions(accountId: String, timeout: Duration?): Map { + val validTimeout = timeout.validateTimeout() + + return withTimeout(validTimeout) { + subscriptionRepository.getAccountActiveSubscriptions(AccountId(accountId)) + .map { subscription -> + val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) + subscription.copy(dappMetaData = metadata) + } + .associateBy { subscription -> subscription.topic.value } + } + } + +} + +internal interface GetActiveSubscriptionsUseCaseInterface { + suspend fun getActiveSubscriptions(accountId: String, timeout: Duration?): Map +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt new file mode 100644 index 000000000..cac3efb47 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt @@ -0,0 +1,22 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.notify.common.model.Subscription +import com.reown.notify.data.storage.SubscriptionRepository + +internal class GetAllActiveSubscriptionsUseCase( + private val subscriptionRepository: SubscriptionRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, +) { + + suspend operator fun invoke(): Map = + subscriptionRepository.getAllActiveSubscriptions() + .map { subscription -> + val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) + subscription.copy(dappMetaData = metadata) + } + .associateBy { subscription -> subscription.topic.value } +} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationHistoryUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationHistoryUseCase.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationHistoryUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationHistoryUseCase.kt index e6a4dc4fd..552f2cc34 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationHistoryUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationHistoryUseCase.kt @@ -1,29 +1,29 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.common.model.GetNotificationHistory -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.notify.common.model.TimeoutInfo -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.blockingCallsDefaultTimeout -import com.walletconnect.notify.engine.blockingCallsDelayInterval -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.responses.OnGetNotificationsResponseUseCase -import com.walletconnect.notify.engine.validateTimeout +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.notify.common.model.GetNotificationHistory +import com.reown.notify.common.model.NotifyRpc +import com.reown.notify.common.model.TimeoutInfo +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.blockingCallsDefaultTimeout +import com.reown.notify.engine.blockingCallsDelayInterval +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.responses.OnGetNotificationsResponseUseCase +import com.reown.notify.engine.validateTimeout import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationTypesUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationTypesUseCase.kt similarity index 85% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationTypesUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationTypesUseCase.kt index 138aa149f..7aef6ceb5 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetNotificationTypesUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/GetNotificationTypesUseCase.kt @@ -1,10 +1,10 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.calls +package com.reown.notify.engine.calls -import com.walletconnect.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase -import com.walletconnect.notify.common.model.NotificationType -import com.walletconnect.notify.engine.validateTimeout +import com.reown.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase +import com.reown.notify.common.model.NotificationType +import com.reown.notify.engine.validateTimeout import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.withTimeout import kotlin.time.Duration diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/IsRegisteredUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/IsRegisteredUseCase.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/IsRegisteredUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/IsRegisteredUseCase.kt index f9aca3207..117b4fa39 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/IsRegisteredUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/IsRegisteredUseCase.kt @@ -1,11 +1,11 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.calls +package com.reown.notify.engine.calls -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.engine.domain.createAuthorizationReCaps +import com.reown.android.internal.common.model.AccountId +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.engine.domain.createAuthorizationReCaps internal class IsRegisteredUseCase( private val registeredAccountsRepository: RegisteredAccountsRepository, diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/PrepareRegistrationUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/PrepareRegistrationUseCase.kt similarity index 77% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/PrepareRegistrationUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/PrepareRegistrationUseCase.kt index 8abdfb7cd..aa9672f83 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/PrepareRegistrationUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/PrepareRegistrationUseCase.kt @@ -1,14 +1,14 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.calls +package com.reown.notify.engine.calls -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.common.model.CacaoPayloadWithIdentityPrivateKey -import com.walletconnect.notify.engine.domain.createAuthorizationReCaps +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.foundation.util.Logger +import com.reown.notify.common.model.CacaoPayloadWithIdentityPrivateKey +import com.reown.notify.engine.domain.createAuthorizationReCaps internal class PrepareRegistrationUseCase( private val identitiesInteractor: IdentitiesInteractor, diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/RegisterUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/RegisterUseCase.kt new file mode 100644 index 000000000..de5a53007 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/RegisterUseCase.kt @@ -0,0 +1,64 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.notify.common.model.CacaoPayloadWithIdentityPrivateKey +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.engine.domain.WatchSubscriptionsUseCase +import kotlinx.coroutines.supervisorScope + +internal class RegisterUseCase( + private val identitiesInteractor: IdentitiesInteractor, + private val registeredAccountsRepository: RegisteredAccountsRepository, + private val watchSubscriptionsUseCase: WatchSubscriptionsUseCase, + private val keyManagementRepository: KeyManagementRepository, + private val projectId: ProjectId, +) : RegisterUseCaseInterface { + + override suspend fun register( + cacaoPayloadWithIdentityPrivateKey: CacaoPayloadWithIdentityPrivateKey, + signature: Cacao.Signature, + onSuccess: (String) -> Unit, + onFailure: (Throwable) -> Unit, + ) = supervisorScope { + val (cacaoPayload, identityPrivateKey) = cacaoPayloadWithIdentityPrivateKey + val accountId = AccountId(Issuer(cacaoPayload.iss).accountId) + + if (!accountId.isValid()) + return@supervisorScope onFailure(IllegalArgumentException("AccountId: ${accountId.value} is not CAIP-10 compliant")) + + val identityPublicKey = runCatching { keyManagementRepository.deriveAndStoreEd25519KeyPair(identityPrivateKey) } + .getOrElse { return@supervisorScope onFailure(IllegalArgumentException("Unable to derive identity key")) } + + runCatching { CacaoVerifier(projectId).verify(Cacao(CacaoType.EIP4361.toHeader(), cacaoPayload, signature)) } + .getOrElse { error -> return@supervisorScope onFailure(IllegalArgumentException("Invalid signature: $error")) } + + identitiesInteractor.registerIdentity(identityPublicKey, cacaoPayload, signature).fold( + onFailure = { error -> onFailure(error) }, + onSuccess = { + runCatching { registeredAccountsRepository.insertOrIgnoreAccount(accountId, identityPublicKey) }.fold( + onFailure = { error -> onFailure(error) }, + onSuccess = { watchSubscriptionsUseCase(accountId, onSuccess = { onSuccess(identityPublicKey.keyAsHex) }, onFailure = { error -> onFailure(error) }) } + ) + } + ) + + } +} + +internal interface RegisterUseCaseInterface { + suspend fun register( + cacaoPayloadWithIdentityPrivateKey: CacaoPayloadWithIdentityPrivateKey, + signature: Cacao.Signature, + onSuccess: (String) -> Unit, + onFailure: (Throwable) -> Unit, + ) +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/SubscribeToDappUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/SubscribeToDappUseCase.kt new file mode 100644 index 000000000..721ef4f3c --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/SubscribeToDappUseCase.kt @@ -0,0 +1,136 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import android.net.Uri +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.monthInSeconds +import com.reown.android.internal.utils.thirtySeconds +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.notify.common.model.CreateSubscription +import com.reown.notify.common.model.NotifyRpc +import com.reown.notify.common.model.Scope +import com.reown.notify.common.model.Subscription +import com.reown.notify.common.model.TimeoutInfo +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.blockingCallsDefaultTimeout +import com.reown.notify.engine.blockingCallsDelayInterval +import com.reown.notify.engine.domain.ExtractMetadataFromConfigUseCase +import com.reown.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.responses.OnSubscribeResponseUseCase +import com.reown.notify.engine.validateTimeout +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeout +import kotlin.time.Duration + +typealias DidJsonPublicKeyPair = Pair + +internal class SubscribeToDappUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val crypto: KeyManagementRepository, + private val extractMetadataFromConfigUseCase: ExtractMetadataFromConfigUseCase, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val extractPublicKeysFromDidJson: ExtractPublicKeysFromDidJsonUseCase, + private val onSubscribeResponseUseCase: OnSubscribeResponseUseCase, + private val subscriptionRepository: SubscriptionRepository, + private val logger: Logger, +) : SubscribeToDappUseCaseInterface { + + override suspend fun subscribeToDapp(dappUri: Uri, account: String, timeout: Duration?): CreateSubscription = supervisorScope { + val result = MutableStateFlow(CreateSubscription.Processing) + var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing + + try { + val validTimeout = timeout.validateTimeout() + val (dappPublicKey, authenticationPublicKey) = extractPublicKeysFromDidJson(dappUri).getOrThrow() + val (dappMetaData, dappScopes) = extractMetadataFromConfigUseCase(dappUri).getOrThrow() + + val subscribeTopic = Topic(sha256(dappPublicKey.keyAsBytes)) + val selfPublicKey = crypto.generateAndStoreX25519KeyPair() + val responseTopic = crypto.generateTopicFromKeyAgreement(selfPublicKey, dappPublicKey) + + metadataStorageRepository.insertOrAbortMetadata(topic = responseTopic, appMetaData = dappMetaData, appMetaDataType = AppMetaDataType.PEER) + + val didJwt = fetchDidJwtInteractor.subscriptionRequest(AccountId(account), authenticationPublicKey, dappUri.toString(), dappScopes.map { it.id }).getOrThrow() + + val params = CoreNotifyParams.SubscribeParams(didJwt.value) + val request = NotifyRpc.NotifySubscribe(params = params) + val irnParams = IrnParams(Tags.NOTIFY_SUBSCRIBE, Ttl(thirtySeconds)) + + result.value = CreateSubscription.Processing + timeoutInfo = TimeoutInfo.Data(request.id, responseTopic) + + onSubscribeResponseUseCase.events + .filter { it.first == params } + .map { it.second } + .filter { it is CreateSubscription.Success || it is CreateSubscription.Error } + .onEach { result.emit(it as CreateSubscription) } + .launchIn(scope) + + val selectedScopes = dappScopes + .associate { remote -> remote.id to Scope.Cached(name = remote.name, description = remote.description, id = remote.id, isSelected = true) } + + // optimistically add active subscription which will be updated on response with the correct expiry + // necessary for welcoming messages that could be sent before the subscription response is received + val activeSubscription = Subscription.Active(AccountId(account), selectedScopes, Expiry(monthInSeconds), authenticationPublicKey, responseTopic, dappMetaData, null) + subscriptionRepository.insertOrAbortSubscription(account, activeSubscription) + jsonRpcInteractor.subscribe(responseTopic) + + jsonRpcInteractor.publishJsonRpcRequest( + topic = subscribeTopic, + params = irnParams, + payload = request, + envelopeType = EnvelopeType.ONE, + participants = Participants(selfPublicKey, dappPublicKey), + onFailure = { error -> result.value = CreateSubscription.Error(error) }, + ) + + withTimeout(validTimeout) { + while (result.value == CreateSubscription.Processing) { + delay(blockingCallsDelayInterval) + } + } + + return@supervisorScope result.value + } catch (e: TimeoutCancellationException) { + with(timeoutInfo as TimeoutInfo.Data) { + jsonRpcInteractor.unsubscribe(topic) + removeOptimisticallyAddedSubscription(topic) + return@supervisorScope CreateSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) + } + } catch (e: Exception) { + return@supervisorScope CreateSubscription.Error(e) + } + } + + private suspend fun removeOptimisticallyAddedSubscription(topic: Topic) = + runCatching { subscriptionRepository.deleteSubscriptionByNotifyTopic(topic.value) }.getOrElse { logger.error("SubscribeToDappUseCase - Error - No subscription found for removal") } +} + +internal interface SubscribeToDappUseCaseInterface { + suspend fun subscribeToDapp(dappUri: Uri, account: String, timeout: Duration? = null): CreateSubscription +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UnregisterUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UnregisterUseCase.kt similarity index 76% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UnregisterUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UnregisterUseCase.kt index 05479871d..75ee98052 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UnregisterUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UnregisterUseCase.kt @@ -1,15 +1,15 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.calls +package com.reown.notify.engine.calls -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.domain.StopWatchingSubscriptionsUseCase +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.foundation.common.model.Topic +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.domain.StopWatchingSubscriptionsUseCase import kotlinx.coroutines.supervisorScope internal class UnregisterUseCase( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UpdateSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UpdateSubscriptionUseCase.kt new file mode 100644 index 000000000..d7aa7f4da --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/calls/UpdateSubscriptionUseCase.kt @@ -0,0 +1,89 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.calls + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.thirtySeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.notify.common.model.NotifyRpc +import com.reown.notify.common.model.TimeoutInfo +import com.reown.notify.common.model.UpdateSubscription +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.blockingCallsDefaultTimeout +import com.reown.notify.engine.blockingCallsDelayInterval +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.responses.OnUpdateResponseUseCase +import com.reown.notify.engine.validateTimeout +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeout +import kotlin.time.Duration + +internal class UpdateSubscriptionUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val subscriptionRepository: SubscriptionRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val onUpdateResponseUseCase: OnUpdateResponseUseCase, +) : UpdateSubscriptionUseCaseInterface { + + override suspend fun update(topic: String, scopes: List, timeout: Duration?): UpdateSubscription = supervisorScope { + val result = MutableStateFlow(UpdateSubscription.Processing) + var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing + try { + val validTimeout = timeout.validateTimeout() + + val subscription = subscriptionRepository.getActiveSubscriptionByNotifyTopic(topic) + ?: throw IllegalStateException("No subscription found for topic $topic") + val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) + ?: throw IllegalStateException("No metadata found for topic $topic") + val didJwt = fetchDidJwtInteractor.updateRequest(subscription.account, metadata.url, subscription.authenticationPublicKey, scopes).getOrThrow() + + val params = CoreNotifyParams.UpdateParams(didJwt.value) + val request = NotifyRpc.NotifyUpdate(params = params) + val irnParams = IrnParams(Tags.NOTIFY_UPDATE, Ttl(thirtySeconds)) + timeoutInfo = TimeoutInfo.Data(request.id, Topic(topic)) + + jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, request, onFailure = { error -> result.value = UpdateSubscription.Error(error) }) + + onUpdateResponseUseCase.events + .filter { it.first == params } + .map { it.second } + .filter { it is UpdateSubscription.Success || it is UpdateSubscription.Error } + .onEach { result.emit(it as UpdateSubscription) } + .launchIn(scope) + + withTimeout(validTimeout) { + while (result.value == UpdateSubscription.Processing) { + delay(blockingCallsDelayInterval) + } + } + + return@supervisorScope result.value + } catch (e: TimeoutCancellationException) { + with(timeoutInfo as TimeoutInfo.Data) { + return@supervisorScope UpdateSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) + } + } catch (e: Exception) { + return@supervisorScope UpdateSubscription.Error(e) + } + } +} + +internal interface UpdateSubscriptionUseCaseInterface { + suspend fun update(topic: String, scopes: List, timeout: Duration? = null): UpdateSubscription +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt similarity index 75% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt index 0ba1d19ca..79cd01a4e 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractMetadataFromConfigUseCase.kt @@ -1,12 +1,12 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.domain +package com.reown.notify.engine.domain import android.net.Uri -import com.walletconnect.android.internal.common.explorer.data.model.ImageUrl -import com.walletconnect.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.notify.common.model.Scope +import com.reown.android.internal.common.explorer.data.model.ImageUrl +import com.reown.android.internal.common.explorer.domain.usecase.GetNotifyConfigUseCase +import com.reown.android.internal.common.model.AppMetaData +import com.reown.notify.common.model.Scope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt similarity index 86% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt index df3052f04..13fdfb791 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/ExtractPublicKeysFromDidJsonUseCase.kt @@ -1,16 +1,16 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.domain +package com.reown.notify.engine.domain import android.net.Uri import android.util.Base64 -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.notify.client.InvalidDidJsonFileException -import com.walletconnect.notify.data.wellknown.did.DidJsonDTO -import com.walletconnect.notify.data.wellknown.did.VerificationMethodDTO -import com.walletconnect.notify.engine.calls.DidJsonPublicKeyPair -import com.walletconnect.util.bytesToHex +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.foundation.common.model.PublicKey +import com.reown.notify.client.InvalidDidJsonFileException +import com.reown.notify.data.wellknown.did.DidJsonDTO +import com.reown.notify.data.wellknown.did.VerificationMethodDTO +import com.reown.notify.engine.calls.DidJsonPublicKeyPair +import com.reown.util.bytesToHex import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.net.URL diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FetchDidJwtInteractor.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FetchDidJwtInteractor.kt similarity index 81% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FetchDidJwtInteractor.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FetchDidJwtInteractor.kt index babd12670..13b9c0257 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FetchDidJwtInteractor.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FetchDidJwtInteractor.kt @@ -1,21 +1,21 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.domain - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.jwt.did.encodeDidJwt -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.DidJwt -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.foundation.common.model.PrivateKey -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.notify.data.jwt.delete.EncodeDeleteRequestJwtUseCase -import com.walletconnect.notify.data.jwt.getNotifications.EncodeGetNotificationsRequestJwtUseCase -import com.walletconnect.notify.data.jwt.message.EncodeMessageResponseJwtUseCase -import com.walletconnect.notify.data.jwt.subscription.EncodeSubscriptionRequestJwtUseCase -import com.walletconnect.notify.data.jwt.subscriptionsChanged.EncodeSubscriptionsChangedResponseJwtUseCase -import com.walletconnect.notify.data.jwt.update.EncodeUpdateRequestJwtUseCase -import com.walletconnect.notify.data.jwt.watchSubscriptions.EncodeWatchSubscriptionsRequestJwtUseCase +package com.reown.notify.engine.domain + +import com.reown.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase +import com.reown.android.internal.common.jwt.did.encodeDidJwt +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.DidJwt +import com.reown.android.keyserver.domain.IdentitiesInteractor +import com.reown.foundation.common.model.PrivateKey +import com.reown.foundation.common.model.PublicKey +import com.reown.notify.data.jwt.delete.EncodeDeleteRequestJwtUseCase +import com.reown.notify.data.jwt.getNotifications.EncodeGetNotificationsRequestJwtUseCase +import com.reown.notify.data.jwt.message.EncodeMessageResponseJwtUseCase +import com.reown.notify.data.jwt.subscription.EncodeSubscriptionRequestJwtUseCase +import com.reown.notify.data.jwt.subscriptionsChanged.EncodeSubscriptionsChangedResponseJwtUseCase +import com.reown.notify.data.jwt.update.EncodeUpdateRequestJwtUseCase +import com.reown.notify.data.jwt.watchSubscriptions.EncodeWatchSubscriptionsRequestJwtUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FindRequestedSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FindRequestedSubscriptionUseCase.kt new file mode 100644 index 000000000..183e88739 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/FindRequestedSubscriptionUseCase.kt @@ -0,0 +1,21 @@ +package com.reown.notify.engine.domain + +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.foundation.util.jwt.encodeEd25519DidKey +import com.reown.notify.common.model.Subscription +import kotlinx.coroutines.supervisorScope + +internal class FindRequestedSubscriptionUseCase( + private val metadataStorageRepository: MetadataStorageRepositoryInterface, +) { + + suspend operator fun invoke(encodedAuthenticationPublicKey: String, subscriptions: List): Subscription.Active = supervisorScope { + val subscription = subscriptions.firstOrNull { encodeEd25519DidKey(it.authenticationPublicKey.keyAsBytes) == encodedAuthenticationPublicKey } + ?: throw Exception("No subscription found for audience $encodedAuthenticationPublicKey") + + val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) + return@supervisorScope subscription.copy(dappMetaData = metadata) + } + +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GenerateAppropriateUriUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GenerateAppropriateUriUseCase.kt similarity index 86% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GenerateAppropriateUriUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GenerateAppropriateUriUseCase.kt index bb2b63d4f..dfbc8a693 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GenerateAppropriateUriUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GenerateAppropriateUriUseCase.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.domain +package com.reown.notify.engine.domain import android.net.Uri diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt new file mode 100644 index 000000000..7500981b3 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt @@ -0,0 +1,20 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.domain + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.utils.getPeerTag +import com.reown.foundation.common.model.Topic + +internal class GetSelfKeyForWatchSubscriptionUseCase( + private val keyManagementRepository: KeyManagementRepository, +) { + suspend operator fun invoke(requestTopic: Topic, accountId: AccountId) = runCatching { + keyManagementRepository.getPublicKey(Pair(accountId, requestTopic).getPeerTag()) + }.getOrElse { + keyManagementRepository.generateAndStoreX25519KeyPair().also { pubKey -> + keyManagementRepository.setKey(pubKey, Pair(accountId, requestTopic).getPeerTag()) + } + } +} diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/SetActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/SetActiveSubscriptionsUseCase.kt new file mode 100644 index 000000000..59927232f --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/SetActiveSubscriptionsUseCase.kt @@ -0,0 +1,68 @@ +package com.reown.notify.engine.domain + +import androidx.core.net.toUri +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.key_chain.KeyStore +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.jwt.decodeEd25519DidKey +import com.reown.notify.common.model.Scope +import com.reown.notify.common.model.ServerSubscription +import com.reown.notify.common.model.Subscription +import com.reown.notify.data.storage.SubscriptionRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class SetActiveSubscriptionsUseCase( + private val subscriptionRepository: SubscriptionRepository, + private val extractMetadataFromConfigUseCase: ExtractMetadataFromConfigUseCase, + private val metadataRepository: MetadataStorageRepositoryInterface, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val keyStore: KeyStore, +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(account: String, serverSubscriptions: List): Result> = supervisorScope { + runCatching { + val activeSubscriptions = serverSubscriptions.map { subscription -> + with(subscription) { + val dappUri = appDomainWithHttps.toUri() + + val (metadata, scopes) = extractMetadataFromConfigUseCase(dappUri).getOrThrow() + val selectedScopes = scopes.associate { remote -> + remote.id to Scope.Cached( + name = remote.name, description = remote.description, id = remote.id, + isSelected = subscription.scope.firstOrNull { serverScope -> serverScope == remote.id } != null + ) + } + + val symmetricKey = SymmetricKey(symKey) + val topic = Topic(sha256(symmetricKey.keyAsBytes)) + + metadataRepository.upsertPeerMetadata(topic, metadata, AppMetaDataType.PEER) + keyStore.setKey(topic.value, symmetricKey) + + Subscription.Active(AccountId(account), selectedScopes, Expiry(expiry), decodeEd25519DidKey(appAuthenticationKey), topic, metadata, null) + } + } + + subscriptionRepository.setActiveSubscriptions(account, activeSubscriptions) + + val subscriptionTopic = activeSubscriptions.map { it.topic.value } + jsonRpcInteractor.batchSubscribe(subscriptionTopic, onFailure = { error -> launch { _events.emit(SDKError(error)) } }) + + return@supervisorScope Result.success(activeSubscriptions) + } + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt new file mode 100644 index 000000000..6f3347530 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt @@ -0,0 +1,25 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.domain + +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.notify.data.storage.RegisteredAccountsRepository +import kotlinx.coroutines.supervisorScope + +internal class StopWatchingSubscriptionsUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val registeredAccountsRepository: RegisteredAccountsRepository, +) { + + suspend operator fun invoke(accountId: AccountId, onFailure: (Throwable) -> Unit) = supervisorScope { + val watchTopic = runCatching { registeredAccountsRepository.getAccountByAccountId(accountId.value).notifyServerWatchTopic } + .getOrElse { error -> return@supervisorScope onFailure(error) } + + if (watchTopic == null) { + return@supervisorScope onFailure(IllegalStateException("Watch topic is null")) + } else { + jsonRpcInteractor.unsubscribe(watchTopic) { error -> onFailure(error) } + } + } +} diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/Utils.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/Utils.kt new file mode 100644 index 000000000..98c72b66c --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/Utils.kt @@ -0,0 +1,6 @@ +package com.reown.notify.engine.domain + +fun createAuthorizationReCaps(): String { + // {"att":{"https://notify.walletconnect.com":{"manage/all-apps-notifications":[{}]}}} + return "urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9ub3RpZnkud2FsbGV0Y29ubmVjdC5jb20iOnsibWFuYWdlL2FsbC1hcHBzLW5vdGlmaWNhdGlvbnMiOlt7fV19fX0" +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt similarity index 83% rename from protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt rename to protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt index 3918cc552..a937d6d02 100644 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsForEveryRegisteredAccountUseCase.kt @@ -1,9 +1,9 @@ @file:JvmSynthetic -package com.walletconnect.notify.engine.domain +package com.reown.notify.engine.domain -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository +import com.reown.foundation.util.Logger +import com.reown.notify.data.storage.RegisteredAccountsRepository import kotlinx.coroutines.supervisorScope internal class WatchSubscriptionsForEveryRegisteredAccountUseCase( diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsUseCase.kt new file mode 100644 index 000000000..d4438c872 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/domain/WatchSubscriptionsUseCase.kt @@ -0,0 +1,59 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.domain + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.thirtySeconds +import com.reown.foundation.common.model.Ttl +import com.reown.notify.common.NotifyServerUrl +import com.reown.notify.common.model.NotifyRpc +import com.reown.notify.data.storage.RegisteredAccountsRepository +import kotlinx.coroutines.supervisorScope + +internal class WatchSubscriptionsUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val keyManagementRepository: KeyManagementRepository, + private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, + private val getSelfKeyForWatchSubscriptionUseCase: GetSelfKeyForWatchSubscriptionUseCase, + private val notifyServerUrl: NotifyServerUrl, + private val registeredAccountsRepository: RegisteredAccountsRepository, +) { + + suspend operator fun invoke(accountId: AccountId, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + val (peerPublicKey, authenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrElse { useCaseError -> return@supervisorScope onFailure(useCaseError) } + + val requestTopic = keyManagementRepository.getTopicFromKey(peerPublicKey) + + val selfPublicKey = getSelfKeyForWatchSubscriptionUseCase(requestTopic, accountId) + val responseTopic = keyManagementRepository.generateTopicFromKeyAgreement(selfPublicKey, peerPublicKey) + + jsonRpcInteractor.subscribe(responseTopic) { error -> onFailure(error) } + + val account = registeredAccountsRepository.getAccountByAccountId(accountId.value) + val didJwt = fetchDidJwtInteractor.watchSubscriptionsRequest(accountId, authenticationPublicKey, account.appDomain) + .getOrElse { error -> return@supervisorScope onFailure(error) } + + registeredAccountsRepository.updateNotifyServerData(accountId, responseTopic, authenticationPublicKey) + val watchSubscriptionsParams = CoreNotifyParams.WatchSubscriptionsParams(didJwt.value) + val request = NotifyRpc.NotifyWatchSubscriptions(params = watchSubscriptionsParams) + val irnParams = IrnParams(Tags.NOTIFY_WATCH_SUBSCRIPTIONS, Ttl(thirtySeconds)) + + jsonRpcInteractor.publishJsonRpcRequest( + topic = requestTopic, + params = irnParams, + payload = request, + envelopeType = EnvelopeType.ONE, + participants = Participants(selfPublicKey, peerPublicKey), + onSuccess = onSuccess, + onFailure = onFailure + ) + } +} diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnMessageUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnMessageUseCase.kt new file mode 100644 index 000000000..9e38089cb --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnMessageUseCase.kt @@ -0,0 +1,100 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.requests + +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.monthInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidWeb +import com.reown.foundation.util.jwt.decodeEd25519DidKey +import com.reown.notify.common.convertToUTF8 +import com.reown.notify.common.model.Notification +import com.reown.notify.common.model.NotificationMessage +import com.reown.notify.data.jwt.message.MessageRequestJwtClaim +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope +import java.net.URI + +internal class OnMessageUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val notificationsRepository: NotificationsRepository, + private val subscriptionRepository: SubscriptionRepository, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val logger: Logger, +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: CoreNotifyParams.MessageParams) = supervisorScope { + try { + val activeSubscription = + subscriptionRepository.getActiveSubscriptionByNotifyTopic(request.topic.value) ?: throw IllegalStateException("No active subscription for topic: ${request.topic.value}") + + val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(activeSubscription.topic, AppMetaDataType.PEER) + ?: throw IllegalStateException("No metadata found for topic ${activeSubscription.topic}") + + val messageJwt = extractVerifiedDidJwtClaims(params.messageAuth).getOrThrow() + messageJwt.throwIfIsInvalid(URI(metadata.url).host, activeSubscription.authenticationPublicKey.keyAsHex) + + with(messageJwt.serverNotification) { + if (!notificationsRepository.doesNotificationsExistsByNotificationId(id)) { + + val notification = Notification( + id = id, topic = request.topic.value, sentAt = sentAt, metadata = metadata, + notificationMessage = NotificationMessage(title = convertToUTF8(title), body = convertToUTF8(body), icon = icon, url = url, type = type), + ) + + notificationsRepository.insertOrReplaceNotification(notification) + _events.emit(notification) + } else { + logger.log("OnMessageUseCase - notification already exists $id") + } + } + + val messageResponseJwt = + fetchDidJwtInteractor.messageResponse(account = activeSubscription.account, app = metadata.url, authenticationKey = activeSubscription.authenticationPublicKey).getOrThrow() + val messageResponseParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = messageResponseJwt.value) + val irnParams = IrnParams(Tags.NOTIFY_MESSAGE_RESPONSE, Ttl(monthInSeconds)) + + jsonRpcInteractor.respondWithParams(request.id, request.topic, messageResponseParams, irnParams, onFailure = { error -> logger.error(error) }) + } catch (e: Exception) { + logger.error(e) + _events.emit(SDKError(e)) + } + } + + private fun MessageRequestJwtClaim.throwIfIsInvalid(expectedApp: String, expectedIssuer: String) { + throwIfBaseIsInvalid() + throwIfAppIsInvalid(expectedApp) + throwIfIssuerIsInvalid(expectedIssuer) + } + + private fun MessageRequestJwtClaim.throwIfAppIsInvalid(expectedAppDomain: String) { + val decodedAppDomain = decodeDidWeb(app) + if (decodedAppDomain != expectedAppDomain) throw IllegalStateException("Invalid app claim was $decodedAppDomain instead of $expectedAppDomain") + } + + + private fun MessageRequestJwtClaim.throwIfIssuerIsInvalid(expectedIssuerAsHex: String) { + val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex + if (decodedIssuerAsHex != expectedIssuerAsHex) throw IllegalStateException("Invalid issuer claim was $decodedIssuerAsHex instead of $expectedIssuerAsHex") + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnSubscriptionsChangedUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnSubscriptionsChangedUseCase.kt new file mode 100644 index 000000000..eedb46921 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/requests/OnSubscriptionsChangedUseCase.kt @@ -0,0 +1,88 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.requests + +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.AccountId +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.foundation.util.jwt.decodeEd25519DidKey +import com.reown.notify.common.NotifyServerUrl +import com.reown.notify.common.model.SubscriptionChanged +import com.reown.notify.data.jwt.subscriptionsChanged.SubscriptionsChangedRequestJwtClaim +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase +import com.reown.notify.engine.domain.FetchDidJwtInteractor +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import com.reown.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSubscriptionsChangedUseCase( + private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, + private val fetchDidJwtInteractor: FetchDidJwtInteractor, + private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, + private val registeredAccountsRepository: RegisteredAccountsRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val watchSubscriptionsForEveryRegisteredAccountUseCase: WatchSubscriptionsForEveryRegisteredAccountUseCase, + private val logger: Logger, + private val notifyServerUrl: NotifyServerUrl, +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: CoreNotifyParams.SubscriptionsChangedParams) = supervisorScope { + try { + val jwtClaims = extractVerifiedDidJwtClaims(params.subscriptionsChangedAuth).getOrThrow() + logger.log("SubscriptionsChangedRequestJwtClaim: ${decodeEd25519DidKey(jwtClaims.audience).keyAsHex} - $jwtClaims") + val authenticationPublicKey = registeredAccountsRepository.getAccountByIdentityKey(decodeEd25519DidKey(jwtClaims.audience).keyAsHex).notifyServerAuthenticationKey + ?: throw IllegalStateException("Cached authentication public key is null") + + jwtClaims.throwIfIsInvalid(authenticationPublicKey.keyAsHex) + + val account = decodeDidPkh(jwtClaims.subject) + val subscriptions = setActiveSubscriptionsUseCase(account, jwtClaims.subscriptions).getOrThrow() + val didJwt = fetchDidJwtInteractor.subscriptionsChangedResponse(AccountId(account), authenticationPublicKey).getOrThrow() + val responseParams = ChatNotifyResponseAuthParams.ResponseAuth(didJwt.value) + val irnParams = IrnParams(Tags.NOTIFY_SUBSCRIPTIONS_CHANGED_RESPONSE, Ttl(fiveMinutesInSeconds)) + + jsonRpcInteractor.respondWithParams(request.id, request.topic, responseParams, irnParams, onFailure = { error -> logger.error(error) }) + + _events.emit(SubscriptionChanged(subscriptions)) + } catch (error: Throwable) { + logger.error(error) + _events.emit(SDKError(error)) + } + } + + + private suspend fun SubscriptionsChangedRequestJwtClaim.throwIfIsInvalid(expectedIssuerAsHex: String) { + throwIfBaseIsInvalid() + throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer(expectedIssuerAsHex) + } + + private suspend fun SubscriptionsChangedRequestJwtClaim.throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer(expectedIssuerAsHex: String) { + + val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex + if (decodedIssuerAsHex != expectedIssuerAsHex) { + val (_, newAuthenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrThrow() + + if (decodedIssuerAsHex == newAuthenticationPublicKey.keyAsHex) + watchSubscriptionsForEveryRegisteredAccountUseCase() + else + throw IllegalStateException("Issuer $decodedIssuerAsHex is not valid with cached $expectedIssuerAsHex or fresh ${newAuthenticationPublicKey.keyAsHex}") + } + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnDeleteResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnDeleteResponseUseCase.kt new file mode 100644 index 000000000..571ff55bc --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnDeleteResponseUseCase.kt @@ -0,0 +1,57 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.notify.common.model.DeleteSubscription +import com.reown.notify.data.jwt.delete.DeleteResponseJwtClaim +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnDeleteResponseUseCase( + private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val notificationsRepository: NotificationsRepository, + private val logger: Logger, +) { + private val _events: MutableSharedFlow> = MutableSharedFlow() + val events: SharedFlow> = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.DeleteParams) = supervisorScope { + val resultEvent = try { + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth + val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() + responseJwtClaim.throwIfBaseIsInvalid() + + jsonRpcInteractor.unsubscribe(wcResponse.topic) + + notificationsRepository.deleteNotificationsByTopic(wcResponse.topic.value) + setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() + + DeleteSubscription.Success(wcResponse.topic.value) + } + + is JsonRpcResponse.JsonRpcError -> DeleteSubscription.Error(Throwable(response.error.message)) + } + } catch (e: Exception) { + logger.error(e) + DeleteSubscription.Error(e) + } + + _events.emit(params to resultEvent) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnGetNotificationsResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnGetNotificationsResponseUseCase.kt new file mode 100644 index 000000000..a84e1e390 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnGetNotificationsResponseUseCase.kt @@ -0,0 +1,71 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.foundation.util.Logger +import com.reown.notify.common.convertToUTF8 +import com.reown.notify.common.model.GetNotificationHistory +import com.reown.notify.common.model.Notification +import com.reown.notify.common.model.NotificationMessage +import com.reown.notify.data.jwt.getNotifications.GetNotificationsResponseJwtClaim +import com.reown.notify.data.storage.NotificationsRepository +import com.reown.notify.data.storage.SubscriptionRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnGetNotificationsResponseUseCase( + private val logger: Logger, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val subscriptionRepository: SubscriptionRepository, + private val notificationsRepository: NotificationsRepository, +) { + + private val _events: MutableSharedFlow> = MutableSharedFlow() + val events: SharedFlow> = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.GetNotificationsParams) = supervisorScope { + val resultEvent = try { + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + val auth = (response.result as ChatNotifyResponseAuthParams.Auth).auth + val responseJwtClaim = extractVerifiedDidJwtClaims(auth).getOrThrow() + responseJwtClaim.throwIfBaseIsInvalid() + + val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(wcResponse.topic, AppMetaDataType.PEER) + ?: throw IllegalStateException("No metadata found for topic ${wcResponse.topic}") + + val notifications = responseJwtClaim.notifications.mapIndexed { index, notification -> + with(notification) { + Notification( + id = id, topic = wcResponse.topic.value, sentAt = sentAt, metadata = metadata, + notificationMessage = NotificationMessage(title = convertToUTF8(title), body = convertToUTF8(body), icon = icon, url = url, type = type), + ) + } + } + + notificationsRepository.insertOrReplaceNotifications(notifications) + if (!responseJwtClaim.hasMore) subscriptionRepository.updateActiveSubscriptionWithLastNotificationId(notifications.lastOrNull()?.id, wcResponse.topic.value) + GetNotificationHistory.Success(notifications, responseJwtClaim.hasMore) + } + + is JsonRpcResponse.JsonRpcError -> GetNotificationHistory.Error(Throwable(response.error.message)) + } + } catch (e: Exception) { + logger.error(e) + GetNotificationHistory.Error(e) + } + + _events.emit(params to resultEvent) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnSubscribeResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnSubscribeResponseUseCase.kt new file mode 100644 index 000000000..b2908586f --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnSubscribeResponseUseCase.kt @@ -0,0 +1,72 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.notify.common.model.CreateSubscription +import com.reown.notify.data.jwt.subscription.SubscriptionRequestJwtClaim +import com.reown.notify.data.jwt.subscription.SubscriptionResponseJwtClaim +import com.reown.notify.data.storage.SubscriptionRepository +import com.reown.notify.engine.domain.FindRequestedSubscriptionUseCase +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSubscribeResponseUseCase( + private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, + private val findRequestedSubscriptionUseCase: FindRequestedSubscriptionUseCase, + private val subscriptionRepository: SubscriptionRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val logger: Logger, +) { + private val _events: MutableSharedFlow> = MutableSharedFlow() + val events: SharedFlow> = _events.asSharedFlow() + + + suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.SubscribeParams) = supervisorScope { + jsonRpcInteractor.unsubscribe(wcResponse.topic) + + val resultEvent = try { + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth + val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() + responseJwtClaim.throwIfBaseIsInvalid() + + val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() + val requestJwtClaim = extractVerifiedDidJwtClaims(params.subscriptionAuth).getOrThrow() + val subscription = findRequestedSubscriptionUseCase(requestJwtClaim.audience, subscriptions) + + CreateSubscription.Success(subscription) + } + + is JsonRpcResponse.JsonRpcError -> { + removeOptimisticallyAddedSubscription(wcResponse.topic) + CreateSubscription.Error(Throwable(response.error.message)) + } + + } + } catch (e: Exception) { + removeOptimisticallyAddedSubscription(wcResponse.topic) + logger.error(e) + CreateSubscription.Error(e) + } + + _events.emit(params to resultEvent) + } + + + private suspend fun removeOptimisticallyAddedSubscription(topic: Topic) = + runCatching { subscriptionRepository.deleteSubscriptionByNotifyTopic(topic.value) }.getOrElse { logger.error("OnSubscribeResponse - Error - No subscription found for removal") } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnUpdateResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnUpdateResponseUseCase.kt new file mode 100644 index 000000000..2d811a6b0 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnUpdateResponseUseCase.kt @@ -0,0 +1,57 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.params.CoreNotifyParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.notify.common.model.CreateSubscription +import com.reown.notify.common.model.UpdateSubscription +import com.reown.notify.data.jwt.update.UpdateRequestJwtClaim +import com.reown.notify.data.jwt.update.UpdateResponseJwtClaim +import com.reown.notify.engine.domain.FindRequestedSubscriptionUseCase +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnUpdateResponseUseCase( + private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, + private val findRequestedSubscriptionUseCase: FindRequestedSubscriptionUseCase, + private val logger: Logger +) { + private val _events: MutableSharedFlow> = MutableSharedFlow() + val events: SharedFlow> = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.UpdateParams) = supervisorScope { + val resultEvent = try { + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth + val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() + responseJwtClaim.throwIfBaseIsInvalid() + + val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() + val requestJwtClaim = extractVerifiedDidJwtClaims(params.updateAuth).getOrThrow() + val subscription = findRequestedSubscriptionUseCase(requestJwtClaim.audience, subscriptions) + + UpdateSubscription.Success(subscription) + } + + is JsonRpcResponse.JsonRpcError -> UpdateSubscription.Error(Throwable(response.error.message)) + } + } catch (e: Exception) { + logger.error(e) + CreateSubscription.Error(e) + } + + _events.emit(params to resultEvent) + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt new file mode 100644 index 000000000..fc0a4a3f3 --- /dev/null +++ b/protocol/notify/src/main/kotlin/com/reown/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt @@ -0,0 +1,81 @@ +@file:JvmSynthetic + +package com.reown.notify.engine.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.jwt.did.extractVerifiedDidJwtClaims +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.ChatNotifyResponseAuthParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.foundation.util.Logger +import com.reown.foundation.util.jwt.decodeDidPkh +import com.reown.foundation.util.jwt.decodeEd25519DidKey +import com.reown.notify.common.NotifyServerUrl +import com.reown.notify.common.model.SubscriptionChanged +import com.reown.notify.data.jwt.watchSubscriptions.WatchSubscriptionsResponseJwtClaim +import com.reown.notify.data.storage.RegisteredAccountsRepository +import com.reown.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase +import com.reown.notify.engine.domain.SetActiveSubscriptionsUseCase +import com.reown.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnWatchSubscriptionsResponseUseCase( + private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, + private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, + private val watchSubscriptionsForEveryRegisteredAccountUseCase: WatchSubscriptionsForEveryRegisteredAccountUseCase, + private val accountsRepository: RegisteredAccountsRepository, + private val notifyServerUrl: NotifyServerUrl, + private val logger: Logger, +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse) = supervisorScope { + val resultEvent = try { + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth + val jwtClaims = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() + + jwtClaims.throwIfIsInvalid() + + val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(jwtClaims.subject), jwtClaims.subscriptions).getOrThrow() + SubscriptionChanged(subscriptions) + } + + is JsonRpcResponse.JsonRpcError -> { + SDKError(Exception(response.errorMessage)) + } + } + } catch (e: Exception) { + logger.error(e) + SDKError(e) + } + + _events.emit(resultEvent) + } + + private suspend fun WatchSubscriptionsResponseJwtClaim.throwIfIsInvalid() { + throwIfBaseIsInvalid() + throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer() + } + + private suspend fun WatchSubscriptionsResponseJwtClaim.throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer() { + val expectedIssuer = runCatching { accountsRepository.getAccountByIdentityKey(decodeEd25519DidKey(audience).keyAsHex).notifyServerAuthenticationKey } + .getOrElse { throw IllegalStateException("Account does not exist in storage for $audience") } ?: throw IllegalStateException("Cached authentication public key is null") + + val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex + if (decodedIssuerAsHex != expectedIssuer.keyAsHex) { + val (_, newAuthenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrThrow() + + if (decodedIssuerAsHex == newAuthenticationPublicKey.keyAsHex) + watchSubscriptionsForEveryRegisteredAccountUseCase() + else + throw IllegalStateException("Issuer $decodedIssuerAsHex is not valid with cached ${expectedIssuer.keyAsHex} or fresh ${newAuthenticationPublicKey.keyAsHex}") + } + } +} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyClient.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyClient.kt deleted file mode 100644 index 9736a7be4..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/NotifyClient.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.notify.client - -object NotifyClient: NotifyInterface by NotifyProtocol.instance { - interface Delegate: NotifyInterface.Delegate -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/cacao/CacaoSigner.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/cacao/CacaoSigner.kt deleted file mode 100644 index e6454e2e9..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/client/cacao/CacaoSigner.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.notify.client.cacao - -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.notify.client.Notify - -object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/JsonRpcMethod.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/JsonRpcMethod.kt deleted file mode 100644 index 54e009fa1..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/JsonRpcMethod.kt +++ /dev/null @@ -1,27 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common - -internal object JsonRpcMethod { - - @get:JvmSynthetic - const val WC_NOTIFY_MESSAGE: String = "wc_notifyMessage" - - @get:JvmSynthetic - const val WC_NOTIFY_DELETE: String = "wc_notifyDelete" - - @get:JvmSynthetic - const val WC_NOTIFY_SUBSCRIBE: String = "wc_notifySubscribe" - - @get:JvmSynthetic - const val WC_NOTIFY_UPDATE: String = "wc_notifyUpdate" - - @get:JvmSynthetic - const val WC_NOTIFY_WATCH_SUBSCRIPTIONS: String = "wc_notifyWatchSubscriptions" - - @get:JvmSynthetic - const val WC_NOTIFY_SUBSCRIPTIONS_CHANGED: String = "wc_notifySubscriptionsChanged" - - @get:JvmSynthetic - const val WC_NOTIFY_GET_NOTIFICATIONS: String = "wc_notifyGetNotifications" -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/PeerError.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/PeerError.kt deleted file mode 100644 index 5c92ca1af..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/PeerError.kt +++ /dev/null @@ -1,36 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common - -import com.walletconnect.android.internal.common.model.type.Error - -// Documentation: https://github.com/WalletConnect/walletconnect-docs/blob/main/docs/specs/clients/notify/error-codes.md -internal sealed class PeerError : Error { - - sealed class User: PeerError() { - data class Rejected(override val message: String) : User() { - override val code: Int = 5000 - } - - data class Unsubscribed(override val message: String) : User() { - override val code: Int = 6000 - } - - data class HasExistingSubscription(override val message: String) : User() { - override val code: Int = 6001 - } - } - - sealed class Failure: PeerError() { - - object ApprovalFailed: Failure() { - override val message: String = "Approval Failed" - override val code: Int = 7002 - } - - object RejectionFailed: Failure() { - override val message: String = "Rejection Failed" - override val code: Int = 7003 - } - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/UtilFunctions.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/UtilFunctions.kt deleted file mode 100644 index 44892c7a9..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/UtilFunctions.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common - -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.utils.monthInSeconds -import java.nio.charset.Charset -import java.util.concurrent.TimeUnit - -@JvmSynthetic -internal fun calcExpiry(): Expiry { - val currentTimeMs = System.currentTimeMillis() - val currentTimeSeconds = TimeUnit.SECONDS.convert(currentTimeMs, TimeUnit.MILLISECONDS) - val expiryTimeSeconds = currentTimeSeconds + monthInSeconds - - return Expiry(expiryTimeSeconds) -} - - -@JvmSynthetic -internal fun convertToUTF8(input: String): String { - val bytes = input.toByteArray(Charset.forName("ISO-8859-1")) - return String(bytes, Charset.forName("UTF-8")) -} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt deleted file mode 100644 index 19aff3d99..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CacaoPayloadWithIdentityPrivateKey.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.foundation.common.model.PrivateKey - -internal data class CacaoPayloadWithIdentityPrivateKey(val payload: Cacao.Payload, val key: PrivateKey) diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CreateSubscription.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CreateSubscription.kt deleted file mode 100644 index 2fb39dc4b..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/CreateSubscription.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal sealed class CreateSubscription : EngineEvent { - - data class Success(val subscription: Subscription.Active) : CreateSubscription() - - data class Error(val throwable: Throwable) : CreateSubscription() - - object Processing : CreateSubscription() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/DeleteSubscription.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/DeleteSubscription.kt deleted file mode 100644 index 4cb821dcb..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/DeleteSubscription.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal sealed class DeleteSubscription : EngineEvent { - - data class Success(val topic: String) : DeleteSubscription() - - data class Error(val throwable: Throwable) : DeleteSubscription() - - object Processing : DeleteSubscription() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/EngineMapper.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/EngineMapper.kt deleted file mode 100644 index d59b52576..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/EngineMapper.kt +++ /dev/null @@ -1,147 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.utils.toClient -import com.walletconnect.notify.client.Notify - -@JvmSynthetic -internal fun Core.Model.Message.Notify.toClient(topic: String): Notify.Model.Notification.Decrypted { - return Notify.Model.Notification.Decrypted(title, body, url, type, topic) -} - -@JvmSynthetic -@Throws(IllegalArgumentException::class) -internal fun Notification.toClient(): Notify.Model.NotificationRecord { - return Notify.Model.NotificationRecord( - id = this.id, - topic = this.topic, - sentAt = this.sentAt, - notification = Notify.Model.Notification.Decrypted( - title = this.notificationMessage.title, - body = this.notificationMessage.body, - url = this.notificationMessage.url, - type = this.notificationMessage.type, - topic = this.topic - ), - metadata = this.metadata?.let { it.toClient() } ?: run { throw IllegalArgumentException("Metadata is null") } - ) -} - - -@JvmSynthetic -internal fun Notification.toCore(): Core.Model.Message.Notify { - return Core.Model.Message.Notify( - title = this.notificationMessage.title, - body = this.notificationMessage.body, - url = this.notificationMessage.url, - type = this.notificationMessage.type, - topic = this.topic - ) -} - - -@JvmSynthetic -internal fun NotificationType.toClient(): Notify.Model.NotificationType { - return Notify.Model.NotificationType(id, name, description, iconUrl) -} - - -@JvmSynthetic -internal fun CacaoPayloadWithIdentityPrivateKey.toClient(): Notify.Model.CacaoPayloadWithIdentityPrivateKey { - return Notify.Model.CacaoPayloadWithIdentityPrivateKey(payload.toClient(), key) -} - - -@JvmSynthetic -internal fun Notify.Model.CacaoPayloadWithIdentityPrivateKey.toCommon(): CacaoPayloadWithIdentityPrivateKey { - return CacaoPayloadWithIdentityPrivateKey(payload.toCommon(), key) -} - - -@JvmSynthetic -internal fun Cacao.Payload.toClient(): Notify.Model.Cacao.Payload { - return Notify.Model.Cacao.Payload(iss, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) -} - - -@JvmSynthetic -internal fun Notify.Model.Cacao.Payload.toCommon(): Cacao.Payload { - return Cacao.Payload(iss, domain, aud, version, nonce, iat, nbf, exp, statement, requestId, resources) -} - - -@JvmSynthetic -internal fun Notify.Model.Cacao.Signature.toCommon(): Cacao.Signature = Cacao.Signature(t, s, m) - - -@JvmSynthetic -internal fun DeleteSubscription.toClient(): Notify.Result.DeleteSubscription = when (this) { - is DeleteSubscription.Success -> Notify.Result.DeleteSubscription.Success(topic) - is DeleteSubscription.Error -> Notify.Result.DeleteSubscription.Error(Notify.Model.Error(throwable)) - DeleteSubscription.Processing -> Notify.Result.DeleteSubscription.Error(Notify.Model.Error(IllegalStateException())) -} - -@JvmSynthetic -internal fun GetNotificationHistory.toClient(): Notify.Result.GetNotificationHistory = when (this) { - is GetNotificationHistory.Success -> Notify.Result.GetNotificationHistory.Success(notifications.toClient(), hasMore) - is GetNotificationHistory.Error -> Notify.Result.GetNotificationHistory.Error(Notify.Model.Error(throwable)) - GetNotificationHistory.Processing -> Notify.Result.GetNotificationHistory.Error(Notify.Model.Error(IllegalStateException())) -} - -@JvmSynthetic -internal fun List.toClient(): List { - return map { it.toClient() } -} - -@JvmSynthetic -internal fun SubscriptionChanged.toClient(): Notify.Event.SubscriptionsChanged = - Notify.Event.SubscriptionsChanged(subscriptions.map { it.toClient() }) - -@JvmSynthetic -internal fun CreateSubscription.toClient(): Notify.Result.Subscribe = when (this) { - is CreateSubscription.Success -> Notify.Result.Subscribe.Success(subscription.toClient()) - is CreateSubscription.Error -> Notify.Result.Subscribe.Error(Notify.Model.Error(throwable)) - CreateSubscription.Processing -> Notify.Result.Subscribe.Error(Notify.Model.Error(IllegalStateException())) -} - -@JvmSynthetic -internal fun Subscription.Active.toClient(): Notify.Model.Subscription { - return Notify.Model.Subscription( - topic = topic.value, - account = account.value, - relay = relay.toClient(), - metadata = dappMetaData.toClient(), - scope = mapOfScope.toClient(), - expiry = expiry.seconds, - ) -} - -@JvmSynthetic -internal fun UpdateSubscription.toClient(): Notify.Result.UpdateSubscription = when (this) { - is UpdateSubscription.Success -> Notify.Result.UpdateSubscription.Success(subscription.toClient()) - is UpdateSubscription.Error -> Notify.Result.UpdateSubscription.Error(Notify.Model.Error(IllegalStateException())) - UpdateSubscription.Processing -> Notify.Result.UpdateSubscription.Error(Notify.Model.Error(IllegalStateException())) - -} - -@JvmSynthetic -internal fun RelayProtocolOptions.toClient(): Notify.Model.Subscription.Relay { - return Notify.Model.Subscription.Relay(protocol, data) -} - -@JvmSynthetic -internal fun Map.toClient(): Map { - return map { (key, value) -> - Notify.Model.Subscription.ScopeId(key) to Notify.Model.Subscription.ScopeSetting(value.name, value.description, value.isSelected) - }.toMap() -} - -@JvmSynthetic -internal fun SDKError.toClient(): Notify.Model.Error { - return Notify.Model.Error(exception) -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Error.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Error.kt deleted file mode 100644 index 033e64fdf..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Error.kt +++ /dev/null @@ -1,10 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal data class Error( - val requestId: Long, - val rejectionReason: String, -) : EngineEvent \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/GetNotificationHistory.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/GetNotificationHistory.kt deleted file mode 100644 index 18613615c..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/GetNotificationHistory.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal sealed class GetNotificationHistory : EngineEvent { - - data class Success(val notifications: List, val hasMore: Boolean) : GetNotificationHistory() - - data class Error(val throwable: Throwable) : GetNotificationHistory() - - object Processing : GetNotificationHistory() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Notification.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Notification.kt deleted file mode 100644 index 8f1b4a769..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Notification.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal data class Notification( - val id: String, - val topic: String, - val sentAt: Long, - val notificationMessage: NotificationMessage, - val metadata: AppMetaData?, -) : EngineEvent \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/RegisteredAccount.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/RegisteredAccount.kt deleted file mode 100644 index 791cf0528..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/RegisteredAccount.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -data class RegisteredAccount( - val accountId: AccountId, - val publicIdentityKey: PublicKey, - val allApps: Boolean, - val appDomain: String?, - val notifyServerWatchTopic: Topic?, - val notifyServerAuthenticationKey: PublicKey?, -) \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Scope.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Scope.kt deleted file mode 100644 index c8e94bf3d..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Scope.kt +++ /dev/null @@ -1,23 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -internal sealed class Scope { - abstract val id: String - abstract val name: String - abstract val description: String - - data class Remote( - override val id: String, - override val name: String, - override val description: String, - val iconUrl: String?, - ) : Scope() - - data class Cached( - override val id: String, - override val name: String, - override val description: String, - val isSelected: Boolean, - ) : Scope() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Subscription.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Subscription.kt deleted file mode 100644 index 510d49ea9..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/Subscription.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic - -internal sealed class Subscription { - abstract val account: AccountId - abstract val mapOfScope: Map - abstract val expiry: Expiry - - data class Active( - override val account: AccountId, - override val mapOfScope: Map, - override val expiry: Expiry, - val authenticationPublicKey: PublicKey, - val topic: Topic, - val dappMetaData: AppMetaData? = null, - val requestedSubscriptionId: Long? = null, - val relay: RelayProtocolOptions = RelayProtocolOptions(), - val lastNotificationId: String? = null, - val reachedEndOfHistory: Boolean = false, - ) : Subscription() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/SubscriptionChanged.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/SubscriptionChanged.kt deleted file mode 100644 index 2d7e7b5e0..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/SubscriptionChanged.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal data class SubscriptionChanged( - val subscriptions: List, -) : EngineEvent diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/TimeoutInfo.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/TimeoutInfo.kt deleted file mode 100644 index 355398f48..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/TimeoutInfo.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.notify.common.model - -import com.walletconnect.foundation.common.model.Topic - -internal sealed interface TimeoutInfo { - data class Data(val requestId: Long, val topic: Topic) : TimeoutInfo - object Nothing : TimeoutInfo -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/UpdateSubscription.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/UpdateSubscription.kt deleted file mode 100644 index 1463cdc51..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/common/model/UpdateSubscription.kt +++ /dev/null @@ -1,14 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.common.model - -import com.walletconnect.android.internal.common.model.type.EngineEvent - -internal sealed class UpdateSubscription : EngineEvent { - - data class Success(val subscription: Subscription.Active) : UpdateSubscription() - - data class Error(val throwable: Throwable) : UpdateSubscription() - - object Processing : UpdateSubscription() -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt deleted file mode 100644 index fb0ce0ecb..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/delete/EncodeDeleteRequestJwtUseCase.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.delete - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeDeleteRequestJwtUseCase( - private val app: String, - private val accountId: AccountId, - private val authenticationKey: PublicKey, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): DeleteRequestJwtClaim = with(params) { - DeleteRequestJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - app = encodeDidWeb(app) - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt deleted file mode 100644 index 355013c31..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/getNotifications/EncodeGetNotificationsRequestJwtUseCase.kt +++ /dev/null @@ -1,33 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.getNotifications - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeGetNotificationsRequestJwtUseCase( - private val app: String, - private val accountId: AccountId, - private val authenticationKey: PublicKey, - private val limit: Int, - private val after: String?, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): GetNotificationsRequestJwtClaim = with(params) { - GetNotificationsRequestJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - app = encodeDidWeb(app), - limit = limit, - after = after, - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt deleted file mode 100644 index 45956e3a7..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/message/EncodeMessageResponseJwtUseCase.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.message - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeMessageResponseJwtUseCase( - private val app: String, - private val accountId: AccountId, - private val authenticationKey: PublicKey, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): MessageResponseJwtClaim = with(params) { - MessageResponseJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - app = encodeDidWeb(app) - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt deleted file mode 100644 index 4217958c7..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscription/EncodeSubscriptionRequestJwtUseCase.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.subscription - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeSubscriptionRequestJwtUseCase( - private val app: String, - private val accountId: AccountId, - private val authenticationKey: PublicKey, - private val scope: String -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): SubscriptionRequestJwtClaim = with(params) { - SubscriptionRequestJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - scope = scope, - app = encodeDidWeb(app) - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt deleted file mode 100644 index 0ae9ae80a..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/subscriptionsChanged/EncodeSubscriptionsChangedResponseJwtUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.subscriptionsChanged - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeSubscriptionsChangedResponseJwtUseCase( - private val accountId: AccountId, - private val authenticationKey: PublicKey, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): SubscriptionsChangedResponseJwtClaim = with(params) { - SubscriptionsChangedResponseJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt deleted file mode 100644 index ff3d57425..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/update/EncodeUpdateRequestJwtUseCase.kt +++ /dev/null @@ -1,31 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.update - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeUpdateRequestJwtUseCase( - private val accountId: AccountId, - private val app: String, - private val authenticationKey: PublicKey, - private val scope: String, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): UpdateRequestJwtClaim = with(params) { - UpdateRequestJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - app = encodeDidWeb(app), - scope = scope, - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt deleted file mode 100644 index 993f44500..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/data/jwt/watchSubscriptions/EncodeWatchSubscriptionsRequestJwtUseCase.kt +++ /dev/null @@ -1,29 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.data.jwt.watchSubscriptions - -import com.walletconnect.android.internal.common.jwt.did.EncodeDidJwtPayloadUseCase -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.jwt.encodeDidPkh -import com.walletconnect.foundation.util.jwt.encodeDidWeb -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey - -internal class EncodeWatchSubscriptionsRequestJwtUseCase( - private val accountId: AccountId, - private val authenticationKey: PublicKey, - private val appDomain: String?, -) : EncodeDidJwtPayloadUseCase { - - override fun invoke(params: EncodeDidJwtPayloadUseCase.Params): WatchSubscriptionsRequestJwtClaim = with(params) { - WatchSubscriptionsRequestJwtClaim( - issuedAt = issuedAt, - expiration = expiration, - issuer = issuer, - keyserverUrl = keyserverUrl, - audience = encodeEd25519DidKey(authenticationKey.keyAsBytes), - subject = encodeDidPkh(accountId.value), - appDidWeb = appDomain?.let { encodeDidWeb(it) } - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/CallModule.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/CallModule.kt deleted file mode 100644 index c129d0a5c..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/di/CallModule.kt +++ /dev/null @@ -1,146 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.notify.engine.calls.DecryptNotifyMessageUseCase -import com.walletconnect.notify.engine.calls.DeleteSubscriptionUseCase -import com.walletconnect.notify.engine.calls.DeleteSubscriptionUseCaseInterface -import com.walletconnect.notify.engine.calls.GetActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.calls.GetActiveSubscriptionsUseCaseInterface -import com.walletconnect.notify.engine.calls.GetNotificationHistoryUseCase -import com.walletconnect.notify.engine.calls.GetNotificationHistoryUseCaseInterface -import com.walletconnect.notify.engine.calls.GetNotificationTypesUseCase -import com.walletconnect.notify.engine.calls.GetNotificationTypesUseCaseInterface -import com.walletconnect.notify.engine.calls.IsRegisteredUseCase -import com.walletconnect.notify.engine.calls.IsRegisteredUseCaseInterface -import com.walletconnect.notify.engine.calls.PrepareRegistrationUseCase -import com.walletconnect.notify.engine.calls.PrepareRegistrationUseCaseInterface -import com.walletconnect.notify.engine.calls.RegisterUseCase -import com.walletconnect.notify.engine.calls.RegisterUseCaseInterface -import com.walletconnect.notify.engine.calls.SubscribeToDappUseCase -import com.walletconnect.notify.engine.calls.SubscribeToDappUseCaseInterface -import com.walletconnect.notify.engine.calls.UnregisterUseCase -import com.walletconnect.notify.engine.calls.UnregisterUseCaseInterface -import com.walletconnect.notify.engine.calls.UpdateSubscriptionUseCase -import com.walletconnect.notify.engine.calls.UpdateSubscriptionUseCaseInterface -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun callModule() = module { - - single { - SubscribeToDappUseCase( - jsonRpcInteractor = get(), - crypto = get(), - extractMetadataFromConfigUseCase = get(), - metadataStorageRepository = get(), - fetchDidJwtInteractor = get(), - extractPublicKeysFromDidJson = get(), - onSubscribeResponseUseCase = get(), - subscriptionRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - UpdateSubscriptionUseCase( - jsonRpcInteractor = get(), - subscriptionRepository = get(), - metadataStorageRepository = get(), - fetchDidJwtInteractor = get(), - onUpdateResponseUseCase = get() - ) - } - - single { - DeleteSubscriptionUseCase( - jsonRpcInteractor = get(), - metadataStorageRepository = get(), - subscriptionRepository = get(), - fetchDidJwtInteractor = get(), - onDeleteResponseUseCase = get() - ) - } - - single(named(AndroidCommonDITags.DECRYPT_NOTIFY_MESSAGE)) { - val useCase = DecryptNotifyMessageUseCase( - codec = get(), - serializer = get(), - jsonRpcHistory = get(), - notificationsRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - metadataStorageRepository = get() - ) - - get>(named(AndroidCommonDITags.DECRYPT_USE_CASES))[Tags.NOTIFY_MESSAGE.id.toString()] = useCase - useCase - } - - single { - RegisterUseCase( - registeredAccountsRepository = get(), - identitiesInteractor = get(), - watchSubscriptionsUseCase = get(), - keyManagementRepository = get(), - projectId = get() - ) - } - - single { - IsRegisteredUseCase( - registeredAccountsRepository = get(), - identitiesInteractor = get(), - identityServerUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)) - ) - } - - single { - PrepareRegistrationUseCase( - identitiesInteractor = get(), - identityServerUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - keyManagementRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - UnregisterUseCase( - registeredAccountsRepository = get(), - stopWatchingSubscriptionsUseCase = get(), - identitiesInteractor = get(), - keyserverUrl = get(named(AndroidCommonDITags.KEYSERVER_URL)), - subscriptionRepository = get(), - notificationsRepository = get(), - jsonRpcInteractor = get() - ) - } - - single { - GetNotificationTypesUseCase( - getNotifyConfigUseCase = get() - ) - } - - single { - GetActiveSubscriptionsUseCase( - subscriptionRepository = get(), - metadataStorageRepository = get() - ) - } - - single { - GetNotificationHistoryUseCase( - jsonRpcInteractor = get(), - subscriptionRepository = get(), - metadataStorageRepository = get(), - notificationsRepository = get(), - fetchDidJwtInteractor = get(), - onGetNotificationsResponseUseCase = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DecryptNotifyMessageUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DecryptNotifyMessageUseCase.kt deleted file mode 100644 index d5c09fe72..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DecryptNotifyMessageUseCase.kt +++ /dev/null @@ -1,73 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.common.model.Notification -import com.walletconnect.notify.common.model.NotificationMessage -import com.walletconnect.notify.common.model.toCore -import com.walletconnect.notify.data.jwt.message.MessageRequestJwtClaim -import com.walletconnect.notify.data.storage.NotificationsRepository -import kotlinx.coroutines.supervisorScope -import org.bouncycastle.util.encoders.Base64 -import kotlin.reflect.safeCast - -internal class DecryptNotifyMessageUseCase( - private val codec: Codec, - private val serializer: JsonRpcSerializer, - private val jsonRpcHistory: JsonRpcHistory, - private val notificationsRepository: NotificationsRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val logger: Logger -) : DecryptMessageUseCaseInterface { - - override suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - try { - val decryptedMessageString = codec.decrypt(Topic(topic), Base64.decode(message)) - val messageHash = sha256(decryptedMessageString.toByteArray()) - - if (messageHash !in jsonRpcHistory.getListOfPendingRecords().map { sha256(it.body.toByteArray()) }) { - val clientJsonRpc = serializer.tryDeserialize(decryptedMessageString) - ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match the Message format: $decryptedMessageString")) - val notifyMessageJwt = CoreNotifyParams.MessageParams::class.safeCast(serializer.deserialize(clientJsonRpc.method, decryptedMessageString)) - ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) - val messageRequestJwt = extractVerifiedDidJwtClaims(notifyMessageJwt.messageAuth).getOrElse { - return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) - } - - val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(Topic(topic), AppMetaDataType.PEER) - ?: return@supervisorScope onFailure(IllegalArgumentException("The decrypted message does not match WalletConnect Notify Message format")) - - with(messageRequestJwt.serverNotification) { - if (!notificationsRepository.doesNotificationsExistsByNotificationId(id)) { - - val notification = Notification( - id = id, topic = topic, sentAt = sentAt, metadata = metadata, notificationMessage = NotificationMessage(title = title, body = body, icon = icon, url = url, type = type) - ) - - notificationsRepository.insertOrReplaceNotification(notification) - onSuccess(notification.toCore()) - } else { - logger.log("DecryptNotifyMessageUseCase - notification already exists $id") - } - } - - } - } catch (e: Exception) { - onFailure(e) - } - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DeleteSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DeleteSubscriptionUseCase.kt deleted file mode 100644 index 6296de29a..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/DeleteSubscriptionUseCase.kt +++ /dev/null @@ -1,89 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.notify.common.model.DeleteSubscription -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.common.model.TimeoutInfo -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.blockingCallsDefaultTimeout -import com.walletconnect.notify.engine.blockingCallsDelayInterval -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.responses.OnDeleteResponseUseCase -import com.walletconnect.notify.engine.validateTimeout -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import kotlin.time.Duration - -internal class DeleteSubscriptionUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val subscriptionRepository: SubscriptionRepository, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val onDeleteResponseUseCase: OnDeleteResponseUseCase, -) : DeleteSubscriptionUseCaseInterface { - - override suspend fun deleteSubscription(topic: String, timeout: Duration?): DeleteSubscription = supervisorScope { - val result = MutableStateFlow(DeleteSubscription.Processing) - var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing - try { - val validTimeout = timeout.validateTimeout() - val activeSubscription: Subscription.Active = subscriptionRepository.getActiveSubscriptionByNotifyTopic(topic) - ?: throw IllegalStateException("Subscription does not exists for $topic") - val dappMetaData = metadataStorageRepository.getByTopicAndType(activeSubscription.topic, AppMetaDataType.PEER) - ?: throw IllegalStateException("Dapp metadata does not exists for $topic") - - val deleteJwt = fetchDidJwtInteractor.deleteRequest(activeSubscription.account, dappMetaData.url, activeSubscription.authenticationPublicKey).getOrThrow() - - val params = CoreNotifyParams.DeleteParams(deleteJwt.value) - val request = NotifyRpc.NotifyDelete(params = params) - val irnParams = IrnParams(Tags.NOTIFY_DELETE, Ttl(monthInSeconds)) - timeoutInfo = TimeoutInfo.Data(request.id, Topic(topic)) - - onDeleteResponseUseCase.events - .filter { it.first == params } - .map { it.second } - .filter { it is DeleteSubscription.Success || it is DeleteSubscription.Error } - .onEach { result.emit(it as DeleteSubscription) } - .launchIn(scope) - - jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, request, onFailure = { error -> result.value = DeleteSubscription.Error(error) }) - - withTimeout(validTimeout) { - while (result.value == DeleteSubscription.Processing) { - delay(blockingCallsDelayInterval) - } - } - - return@supervisorScope result.value - } catch (e: TimeoutCancellationException) { - with(timeoutInfo as TimeoutInfo.Data) { - return@supervisorScope DeleteSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) - } - } catch (e: Exception) { - return@supervisorScope DeleteSubscription.Error(e) - } - } -} - -internal interface DeleteSubscriptionUseCaseInterface { - suspend fun deleteSubscription(topic: String, timeout: Duration? = null): DeleteSubscription -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetActiveSubscriptionsUseCase.kt deleted file mode 100644 index b3b516294..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetActiveSubscriptionsUseCase.kt +++ /dev/null @@ -1,38 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.validateTimeout -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.withTimeout -import kotlin.time.Duration - -internal class GetActiveSubscriptionsUseCase( - private val subscriptionRepository: SubscriptionRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, -) : GetActiveSubscriptionsUseCaseInterface { - - @Throws(TimeoutCancellationException::class) - override suspend fun getActiveSubscriptions(accountId: String, timeout: Duration?): Map { - val validTimeout = timeout.validateTimeout() - - return withTimeout(validTimeout) { - subscriptionRepository.getAccountActiveSubscriptions(AccountId(accountId)) - .map { subscription -> - val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) - subscription.copy(dappMetaData = metadata) - } - .associateBy { subscription -> subscription.topic.value } - } - } - -} - -internal interface GetActiveSubscriptionsUseCaseInterface { - suspend fun getActiveSubscriptions(accountId: String, timeout: Duration?): Map -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt deleted file mode 100644 index d1432807b..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/GetAllActiveSubscriptionsUseCase.kt +++ /dev/null @@ -1,22 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.data.storage.SubscriptionRepository - -internal class GetAllActiveSubscriptionsUseCase( - private val subscriptionRepository: SubscriptionRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, -) { - - suspend operator fun invoke(): Map = - subscriptionRepository.getAllActiveSubscriptions() - .map { subscription -> - val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) - subscription.copy(dappMetaData = metadata) - } - .associateBy { subscription -> subscription.topic.value } -} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/RegisterUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/RegisterUseCase.kt deleted file mode 100644 index d06268b11..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/RegisterUseCase.kt +++ /dev/null @@ -1,64 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.keyserver.domain.IdentitiesInteractor -import com.walletconnect.notify.common.model.CacaoPayloadWithIdentityPrivateKey -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.engine.domain.WatchSubscriptionsUseCase -import kotlinx.coroutines.supervisorScope - -internal class RegisterUseCase( - private val identitiesInteractor: IdentitiesInteractor, - private val registeredAccountsRepository: RegisteredAccountsRepository, - private val watchSubscriptionsUseCase: WatchSubscriptionsUseCase, - private val keyManagementRepository: KeyManagementRepository, - private val projectId: ProjectId, -) : RegisterUseCaseInterface { - - override suspend fun register( - cacaoPayloadWithIdentityPrivateKey: CacaoPayloadWithIdentityPrivateKey, - signature: Cacao.Signature, - onSuccess: (String) -> Unit, - onFailure: (Throwable) -> Unit, - ) = supervisorScope { - val (cacaoPayload, identityPrivateKey) = cacaoPayloadWithIdentityPrivateKey - val accountId = AccountId(Issuer(cacaoPayload.iss).accountId) - - if (!accountId.isValid()) - return@supervisorScope onFailure(IllegalArgumentException("AccountId: ${accountId.value} is not CAIP-10 compliant")) - - val identityPublicKey = runCatching { keyManagementRepository.deriveAndStoreEd25519KeyPair(identityPrivateKey) } - .getOrElse { return@supervisorScope onFailure(IllegalArgumentException("Unable to derive identity key")) } - - runCatching { CacaoVerifier(projectId).verify(Cacao(CacaoType.EIP4361.toHeader(), cacaoPayload, signature)) } - .getOrElse { error -> return@supervisorScope onFailure(IllegalArgumentException("Invalid signature: $error")) } - - identitiesInteractor.registerIdentity(identityPublicKey, cacaoPayload, signature).fold( - onFailure = { error -> onFailure(error) }, - onSuccess = { - runCatching { registeredAccountsRepository.insertOrIgnoreAccount(accountId, identityPublicKey) }.fold( - onFailure = { error -> onFailure(error) }, - onSuccess = { watchSubscriptionsUseCase(accountId, onSuccess = { onSuccess(identityPublicKey.keyAsHex) }, onFailure = { error -> onFailure(error) }) } - ) - } - ) - - } -} - -internal interface RegisterUseCaseInterface { - suspend fun register( - cacaoPayloadWithIdentityPrivateKey: CacaoPayloadWithIdentityPrivateKey, - signature: Cacao.Signature, - onSuccess: (String) -> Unit, - onFailure: (Throwable) -> Unit, - ) -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/SubscribeToDappUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/SubscribeToDappUseCase.kt deleted file mode 100644 index bc8b771fa..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/SubscribeToDappUseCase.kt +++ /dev/null @@ -1,136 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import android.net.Uri -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.common.model.CreateSubscription -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.notify.common.model.Scope -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.common.model.TimeoutInfo -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.blockingCallsDefaultTimeout -import com.walletconnect.notify.engine.blockingCallsDelayInterval -import com.walletconnect.notify.engine.domain.ExtractMetadataFromConfigUseCase -import com.walletconnect.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.responses.OnSubscribeResponseUseCase -import com.walletconnect.notify.engine.validateTimeout -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import kotlin.time.Duration - -typealias DidJsonPublicKeyPair = Pair - -internal class SubscribeToDappUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val crypto: KeyManagementRepository, - private val extractMetadataFromConfigUseCase: ExtractMetadataFromConfigUseCase, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val extractPublicKeysFromDidJson: ExtractPublicKeysFromDidJsonUseCase, - private val onSubscribeResponseUseCase: OnSubscribeResponseUseCase, - private val subscriptionRepository: SubscriptionRepository, - private val logger: Logger, -) : SubscribeToDappUseCaseInterface { - - override suspend fun subscribeToDapp(dappUri: Uri, account: String, timeout: Duration?): CreateSubscription = supervisorScope { - val result = MutableStateFlow(CreateSubscription.Processing) - var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing - - try { - val validTimeout = timeout.validateTimeout() - val (dappPublicKey, authenticationPublicKey) = extractPublicKeysFromDidJson(dappUri).getOrThrow() - val (dappMetaData, dappScopes) = extractMetadataFromConfigUseCase(dappUri).getOrThrow() - - val subscribeTopic = Topic(sha256(dappPublicKey.keyAsBytes)) - val selfPublicKey = crypto.generateAndStoreX25519KeyPair() - val responseTopic = crypto.generateTopicFromKeyAgreement(selfPublicKey, dappPublicKey) - - metadataStorageRepository.insertOrAbortMetadata(topic = responseTopic, appMetaData = dappMetaData, appMetaDataType = AppMetaDataType.PEER) - - val didJwt = fetchDidJwtInteractor.subscriptionRequest(AccountId(account), authenticationPublicKey, dappUri.toString(), dappScopes.map { it.id }).getOrThrow() - - val params = CoreNotifyParams.SubscribeParams(didJwt.value) - val request = NotifyRpc.NotifySubscribe(params = params) - val irnParams = IrnParams(Tags.NOTIFY_SUBSCRIBE, Ttl(thirtySeconds)) - - result.value = CreateSubscription.Processing - timeoutInfo = TimeoutInfo.Data(request.id, responseTopic) - - onSubscribeResponseUseCase.events - .filter { it.first == params } - .map { it.second } - .filter { it is CreateSubscription.Success || it is CreateSubscription.Error } - .onEach { result.emit(it as CreateSubscription) } - .launchIn(scope) - - val selectedScopes = dappScopes - .associate { remote -> remote.id to Scope.Cached(name = remote.name, description = remote.description, id = remote.id, isSelected = true) } - - // optimistically add active subscription which will be updated on response with the correct expiry - // necessary for welcoming messages that could be sent before the subscription response is received - val activeSubscription = Subscription.Active(AccountId(account), selectedScopes, Expiry(monthInSeconds), authenticationPublicKey, responseTopic, dappMetaData, null) - subscriptionRepository.insertOrAbortSubscription(account, activeSubscription) - jsonRpcInteractor.subscribe(responseTopic) - - jsonRpcInteractor.publishJsonRpcRequest( - topic = subscribeTopic, - params = irnParams, - payload = request, - envelopeType = EnvelopeType.ONE, - participants = Participants(selfPublicKey, dappPublicKey), - onFailure = { error -> result.value = CreateSubscription.Error(error) }, - ) - - withTimeout(validTimeout) { - while (result.value == CreateSubscription.Processing) { - delay(blockingCallsDelayInterval) - } - } - - return@supervisorScope result.value - } catch (e: TimeoutCancellationException) { - with(timeoutInfo as TimeoutInfo.Data) { - jsonRpcInteractor.unsubscribe(topic) - removeOptimisticallyAddedSubscription(topic) - return@supervisorScope CreateSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) - } - } catch (e: Exception) { - return@supervisorScope CreateSubscription.Error(e) - } - } - - private suspend fun removeOptimisticallyAddedSubscription(topic: Topic) = - runCatching { subscriptionRepository.deleteSubscriptionByNotifyTopic(topic.value) }.getOrElse { logger.error("SubscribeToDappUseCase - Error - No subscription found for removal") } -} - -internal interface SubscribeToDappUseCaseInterface { - suspend fun subscribeToDapp(dappUri: Uri, account: String, timeout: Duration? = null): CreateSubscription -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UpdateSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UpdateSubscriptionUseCase.kt deleted file mode 100644 index d0e94f179..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/calls/UpdateSubscriptionUseCase.kt +++ /dev/null @@ -1,89 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.calls - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.notify.common.model.TimeoutInfo -import com.walletconnect.notify.common.model.UpdateSubscription -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.blockingCallsDefaultTimeout -import com.walletconnect.notify.engine.blockingCallsDelayInterval -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.responses.OnUpdateResponseUseCase -import com.walletconnect.notify.engine.validateTimeout -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import kotlin.time.Duration - -internal class UpdateSubscriptionUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val subscriptionRepository: SubscriptionRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val onUpdateResponseUseCase: OnUpdateResponseUseCase, -) : UpdateSubscriptionUseCaseInterface { - - override suspend fun update(topic: String, scopes: List, timeout: Duration?): UpdateSubscription = supervisorScope { - val result = MutableStateFlow(UpdateSubscription.Processing) - var timeoutInfo: TimeoutInfo = TimeoutInfo.Nothing - try { - val validTimeout = timeout.validateTimeout() - - val subscription = subscriptionRepository.getActiveSubscriptionByNotifyTopic(topic) - ?: throw IllegalStateException("No subscription found for topic $topic") - val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) - ?: throw IllegalStateException("No metadata found for topic $topic") - val didJwt = fetchDidJwtInteractor.updateRequest(subscription.account, metadata.url, subscription.authenticationPublicKey, scopes).getOrThrow() - - val params = CoreNotifyParams.UpdateParams(didJwt.value) - val request = NotifyRpc.NotifyUpdate(params = params) - val irnParams = IrnParams(Tags.NOTIFY_UPDATE, Ttl(thirtySeconds)) - timeoutInfo = TimeoutInfo.Data(request.id, Topic(topic)) - - jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, request, onFailure = { error -> result.value = UpdateSubscription.Error(error) }) - - onUpdateResponseUseCase.events - .filter { it.first == params } - .map { it.second } - .filter { it is UpdateSubscription.Success || it is UpdateSubscription.Error } - .onEach { result.emit(it as UpdateSubscription) } - .launchIn(scope) - - withTimeout(validTimeout) { - while (result.value == UpdateSubscription.Processing) { - delay(blockingCallsDelayInterval) - } - } - - return@supervisorScope result.value - } catch (e: TimeoutCancellationException) { - with(timeoutInfo as TimeoutInfo.Data) { - return@supervisorScope UpdateSubscription.Error(Throwable("Request: $requestId timed out after ${timeout ?: blockingCallsDefaultTimeout}")) - } - } catch (e: Exception) { - return@supervisorScope UpdateSubscription.Error(e) - } - } -} - -internal interface UpdateSubscriptionUseCaseInterface { - suspend fun update(topic: String, scopes: List, timeout: Duration? = null): UpdateSubscription -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FindRequestedSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FindRequestedSubscriptionUseCase.kt deleted file mode 100644 index 0d73c6215..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/FindRequestedSubscriptionUseCase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.walletconnect.notify.engine.domain - -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.foundation.util.jwt.encodeEd25519DidKey -import com.walletconnect.notify.common.model.Subscription -import kotlinx.coroutines.supervisorScope - -internal class FindRequestedSubscriptionUseCase( - private val metadataStorageRepository: MetadataStorageRepositoryInterface, -) { - - suspend operator fun invoke(encodedAuthenticationPublicKey: String, subscriptions: List): Subscription.Active = supervisorScope { - val subscription = subscriptions.firstOrNull { encodeEd25519DidKey(it.authenticationPublicKey.keyAsBytes) == encodedAuthenticationPublicKey } - ?: throw Exception("No subscription found for audience $encodedAuthenticationPublicKey") - - val metadata = metadataStorageRepository.getByTopicAndType(subscription.topic, AppMetaDataType.PEER) - return@supervisorScope subscription.copy(dappMetaData = metadata) - } - -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt deleted file mode 100644 index be2003ba5..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/GetSelfKeyForWatchSubscriptionUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.domain - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.utils.getPeerTag -import com.walletconnect.foundation.common.model.Topic - -internal class GetSelfKeyForWatchSubscriptionUseCase( - private val keyManagementRepository: KeyManagementRepository, -) { - suspend operator fun invoke(requestTopic: Topic, accountId: AccountId) = runCatching { - keyManagementRepository.getPublicKey(Pair(accountId, requestTopic).getPeerTag()) - }.getOrElse { - keyManagementRepository.generateAndStoreX25519KeyPair().also { pubKey -> - keyManagementRepository.setKey(pubKey, Pair(accountId, requestTopic).getPeerTag()) - } - } -} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/SetActiveSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/SetActiveSubscriptionsUseCase.kt deleted file mode 100644 index 934f7317e..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/SetActiveSubscriptionsUseCase.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.walletconnect.notify.engine.domain - -import androidx.core.net.toUri -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.key_chain.KeyStore -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.jwt.decodeEd25519DidKey -import com.walletconnect.notify.common.model.Scope -import com.walletconnect.notify.common.model.ServerSubscription -import com.walletconnect.notify.common.model.Subscription -import com.walletconnect.notify.data.storage.SubscriptionRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class SetActiveSubscriptionsUseCase( - private val subscriptionRepository: SubscriptionRepository, - private val extractMetadataFromConfigUseCase: ExtractMetadataFromConfigUseCase, - private val metadataRepository: MetadataStorageRepositoryInterface, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val keyStore: KeyStore, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(account: String, serverSubscriptions: List): Result> = supervisorScope { - runCatching { - val activeSubscriptions = serverSubscriptions.map { subscription -> - with(subscription) { - val dappUri = appDomainWithHttps.toUri() - - val (metadata, scopes) = extractMetadataFromConfigUseCase(dappUri).getOrThrow() - val selectedScopes = scopes.associate { remote -> - remote.id to Scope.Cached( - name = remote.name, description = remote.description, id = remote.id, - isSelected = subscription.scope.firstOrNull { serverScope -> serverScope == remote.id } != null - ) - } - - val symmetricKey = SymmetricKey(symKey) - val topic = Topic(sha256(symmetricKey.keyAsBytes)) - - metadataRepository.upsertPeerMetadata(topic, metadata, AppMetaDataType.PEER) - keyStore.setKey(topic.value, symmetricKey) - - Subscription.Active(AccountId(account), selectedScopes, Expiry(expiry), decodeEd25519DidKey(appAuthenticationKey), topic, metadata, null) - } - } - - subscriptionRepository.setActiveSubscriptions(account, activeSubscriptions) - - val subscriptionTopic = activeSubscriptions.map { it.topic.value } - jsonRpcInteractor.batchSubscribe(subscriptionTopic, onFailure = { error -> launch { _events.emit(SDKError(error)) } }) - - return@supervisorScope Result.success(activeSubscriptions) - } - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt deleted file mode 100644 index 65dae3126..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/StopWatchingSubscriptionsUseCase.kt +++ /dev/null @@ -1,25 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.domain - -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import kotlinx.coroutines.supervisorScope - -internal class StopWatchingSubscriptionsUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val registeredAccountsRepository: RegisteredAccountsRepository, -) { - - suspend operator fun invoke(accountId: AccountId, onFailure: (Throwable) -> Unit) = supervisorScope { - val watchTopic = runCatching { registeredAccountsRepository.getAccountByAccountId(accountId.value).notifyServerWatchTopic } - .getOrElse { error -> return@supervisorScope onFailure(error) } - - if (watchTopic == null) { - return@supervisorScope onFailure(IllegalStateException("Watch topic is null")) - } else { - jsonRpcInteractor.unsubscribe(watchTopic) { error -> onFailure(error) } - } - } -} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/Utils.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/Utils.kt deleted file mode 100644 index c6bed3684..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/Utils.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.notify.engine.domain - -fun createAuthorizationReCaps(): String { - // {"att":{"https://notify.walletconnect.com":{"manage/all-apps-notifications":[{}]}}} - return "urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9ub3RpZnkud2FsbGV0Y29ubmVjdC5jb20iOnsibWFuYWdlL2FsbC1hcHBzLW5vdGlmaWNhdGlvbnMiOlt7fV19fX0" -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsUseCase.kt deleted file mode 100644 index da68fcd12..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/domain/WatchSubscriptionsUseCase.kt +++ /dev/null @@ -1,59 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.domain - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.notify.common.NotifyServerUrl -import com.walletconnect.notify.common.model.NotifyRpc -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import kotlinx.coroutines.supervisorScope - -internal class WatchSubscriptionsUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val keyManagementRepository: KeyManagementRepository, - private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, - private val getSelfKeyForWatchSubscriptionUseCase: GetSelfKeyForWatchSubscriptionUseCase, - private val notifyServerUrl: NotifyServerUrl, - private val registeredAccountsRepository: RegisteredAccountsRepository, -) { - - suspend operator fun invoke(accountId: AccountId, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - val (peerPublicKey, authenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrElse { useCaseError -> return@supervisorScope onFailure(useCaseError) } - - val requestTopic = keyManagementRepository.getTopicFromKey(peerPublicKey) - - val selfPublicKey = getSelfKeyForWatchSubscriptionUseCase(requestTopic, accountId) - val responseTopic = keyManagementRepository.generateTopicFromKeyAgreement(selfPublicKey, peerPublicKey) - - jsonRpcInteractor.subscribe(responseTopic) { error -> onFailure(error) } - - val account = registeredAccountsRepository.getAccountByAccountId(accountId.value) - val didJwt = fetchDidJwtInteractor.watchSubscriptionsRequest(accountId, authenticationPublicKey, account.appDomain) - .getOrElse { error -> return@supervisorScope onFailure(error) } - - registeredAccountsRepository.updateNotifyServerData(accountId, responseTopic, authenticationPublicKey) - val watchSubscriptionsParams = CoreNotifyParams.WatchSubscriptionsParams(didJwt.value) - val request = NotifyRpc.NotifyWatchSubscriptions(params = watchSubscriptionsParams) - val irnParams = IrnParams(Tags.NOTIFY_WATCH_SUBSCRIPTIONS, Ttl(thirtySeconds)) - - jsonRpcInteractor.publishJsonRpcRequest( - topic = requestTopic, - params = irnParams, - payload = request, - envelopeType = EnvelopeType.ONE, - participants = Participants(selfPublicKey, peerPublicKey), - onSuccess = onSuccess, - onFailure = onFailure - ) - } -} diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnMessageUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnMessageUseCase.kt deleted file mode 100644 index dc73fe1a6..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnMessageUseCase.kt +++ /dev/null @@ -1,100 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.requests - -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidWeb -import com.walletconnect.foundation.util.jwt.decodeEd25519DidKey -import com.walletconnect.notify.common.convertToUTF8 -import com.walletconnect.notify.common.model.Notification -import com.walletconnect.notify.common.model.NotificationMessage -import com.walletconnect.notify.data.jwt.message.MessageRequestJwtClaim -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope -import java.net.URI - -internal class OnMessageUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val notificationsRepository: NotificationsRepository, - private val subscriptionRepository: SubscriptionRepository, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val logger: Logger, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: CoreNotifyParams.MessageParams) = supervisorScope { - try { - val activeSubscription = - subscriptionRepository.getActiveSubscriptionByNotifyTopic(request.topic.value) ?: throw IllegalStateException("No active subscription for topic: ${request.topic.value}") - - val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(activeSubscription.topic, AppMetaDataType.PEER) - ?: throw IllegalStateException("No metadata found for topic ${activeSubscription.topic}") - - val messageJwt = extractVerifiedDidJwtClaims(params.messageAuth).getOrThrow() - messageJwt.throwIfIsInvalid(URI(metadata.url).host, activeSubscription.authenticationPublicKey.keyAsHex) - - with(messageJwt.serverNotification) { - if (!notificationsRepository.doesNotificationsExistsByNotificationId(id)) { - - val notification = Notification( - id = id, topic = request.topic.value, sentAt = sentAt, metadata = metadata, - notificationMessage = NotificationMessage(title = convertToUTF8(title), body = convertToUTF8(body), icon = icon, url = url, type = type), - ) - - notificationsRepository.insertOrReplaceNotification(notification) - _events.emit(notification) - } else { - logger.log("OnMessageUseCase - notification already exists $id") - } - } - - val messageResponseJwt = - fetchDidJwtInteractor.messageResponse(account = activeSubscription.account, app = metadata.url, authenticationKey = activeSubscription.authenticationPublicKey).getOrThrow() - val messageResponseParams = ChatNotifyResponseAuthParams.ResponseAuth(responseAuth = messageResponseJwt.value) - val irnParams = IrnParams(Tags.NOTIFY_MESSAGE_RESPONSE, Ttl(monthInSeconds)) - - jsonRpcInteractor.respondWithParams(request.id, request.topic, messageResponseParams, irnParams, onFailure = { error -> logger.error(error) }) - } catch (e: Exception) { - logger.error(e) - _events.emit(SDKError(e)) - } - } - - private fun MessageRequestJwtClaim.throwIfIsInvalid(expectedApp: String, expectedIssuer: String) { - throwIfBaseIsInvalid() - throwIfAppIsInvalid(expectedApp) - throwIfIssuerIsInvalid(expectedIssuer) - } - - private fun MessageRequestJwtClaim.throwIfAppIsInvalid(expectedAppDomain: String) { - val decodedAppDomain = decodeDidWeb(app) - if (decodedAppDomain != expectedAppDomain) throw IllegalStateException("Invalid app claim was $decodedAppDomain instead of $expectedAppDomain") - } - - - private fun MessageRequestJwtClaim.throwIfIssuerIsInvalid(expectedIssuerAsHex: String) { - val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex - if (decodedIssuerAsHex != expectedIssuerAsHex) throw IllegalStateException("Invalid issuer claim was $decodedIssuerAsHex instead of $expectedIssuerAsHex") - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnSubscriptionsChangedUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnSubscriptionsChangedUseCase.kt deleted file mode 100644 index 2ce3a7c5e..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/requests/OnSubscriptionsChangedUseCase.kt +++ /dev/null @@ -1,88 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.requests - -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.AccountId -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.foundation.util.jwt.decodeEd25519DidKey -import com.walletconnect.notify.common.NotifyServerUrl -import com.walletconnect.notify.common.model.SubscriptionChanged -import com.walletconnect.notify.data.jwt.subscriptionsChanged.SubscriptionsChangedRequestJwtClaim -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase -import com.walletconnect.notify.engine.domain.FetchDidJwtInteractor -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSubscriptionsChangedUseCase( - private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, - private val fetchDidJwtInteractor: FetchDidJwtInteractor, - private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, - private val registeredAccountsRepository: RegisteredAccountsRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val watchSubscriptionsForEveryRegisteredAccountUseCase: WatchSubscriptionsForEveryRegisteredAccountUseCase, - private val logger: Logger, - private val notifyServerUrl: NotifyServerUrl, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: CoreNotifyParams.SubscriptionsChangedParams) = supervisorScope { - try { - val jwtClaims = extractVerifiedDidJwtClaims(params.subscriptionsChangedAuth).getOrThrow() - logger.log("SubscriptionsChangedRequestJwtClaim: ${decodeEd25519DidKey(jwtClaims.audience).keyAsHex} - $jwtClaims") - val authenticationPublicKey = registeredAccountsRepository.getAccountByIdentityKey(decodeEd25519DidKey(jwtClaims.audience).keyAsHex).notifyServerAuthenticationKey - ?: throw IllegalStateException("Cached authentication public key is null") - - jwtClaims.throwIfIsInvalid(authenticationPublicKey.keyAsHex) - - val account = decodeDidPkh(jwtClaims.subject) - val subscriptions = setActiveSubscriptionsUseCase(account, jwtClaims.subscriptions).getOrThrow() - val didJwt = fetchDidJwtInteractor.subscriptionsChangedResponse(AccountId(account), authenticationPublicKey).getOrThrow() - val responseParams = ChatNotifyResponseAuthParams.ResponseAuth(didJwt.value) - val irnParams = IrnParams(Tags.NOTIFY_SUBSCRIPTIONS_CHANGED_RESPONSE, Ttl(fiveMinutesInSeconds)) - - jsonRpcInteractor.respondWithParams(request.id, request.topic, responseParams, irnParams, onFailure = { error -> logger.error(error) }) - - _events.emit(SubscriptionChanged(subscriptions)) - } catch (error: Throwable) { - logger.error(error) - _events.emit(SDKError(error)) - } - } - - - private suspend fun SubscriptionsChangedRequestJwtClaim.throwIfIsInvalid(expectedIssuerAsHex: String) { - throwIfBaseIsInvalid() - throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer(expectedIssuerAsHex) - } - - private suspend fun SubscriptionsChangedRequestJwtClaim.throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer(expectedIssuerAsHex: String) { - - val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex - if (decodedIssuerAsHex != expectedIssuerAsHex) { - val (_, newAuthenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrThrow() - - if (decodedIssuerAsHex == newAuthenticationPublicKey.keyAsHex) - watchSubscriptionsForEveryRegisteredAccountUseCase() - else - throw IllegalStateException("Issuer $decodedIssuerAsHex is not valid with cached $expectedIssuerAsHex or fresh ${newAuthenticationPublicKey.keyAsHex}") - } - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnDeleteResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnDeleteResponseUseCase.kt deleted file mode 100644 index d1807a6e6..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnDeleteResponseUseCase.kt +++ /dev/null @@ -1,57 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.notify.common.model.DeleteSubscription -import com.walletconnect.notify.data.jwt.delete.DeleteResponseJwtClaim -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnDeleteResponseUseCase( - private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val notificationsRepository: NotificationsRepository, - private val logger: Logger, -) { - private val _events: MutableSharedFlow> = MutableSharedFlow() - val events: SharedFlow> = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.DeleteParams) = supervisorScope { - val resultEvent = try { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth - val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() - responseJwtClaim.throwIfBaseIsInvalid() - - jsonRpcInteractor.unsubscribe(wcResponse.topic) - - notificationsRepository.deleteNotificationsByTopic(wcResponse.topic.value) - setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() - - DeleteSubscription.Success(wcResponse.topic.value) - } - - is JsonRpcResponse.JsonRpcError -> DeleteSubscription.Error(Throwable(response.error.message)) - } - } catch (e: Exception) { - logger.error(e) - DeleteSubscription.Error(e) - } - - _events.emit(params to resultEvent) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnGetNotificationsResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnGetNotificationsResponseUseCase.kt deleted file mode 100644 index 6e326f576..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnGetNotificationsResponseUseCase.kt +++ /dev/null @@ -1,71 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.common.convertToUTF8 -import com.walletconnect.notify.common.model.GetNotificationHistory -import com.walletconnect.notify.common.model.Notification -import com.walletconnect.notify.common.model.NotificationMessage -import com.walletconnect.notify.data.jwt.getNotifications.GetNotificationsResponseJwtClaim -import com.walletconnect.notify.data.storage.NotificationsRepository -import com.walletconnect.notify.data.storage.SubscriptionRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnGetNotificationsResponseUseCase( - private val logger: Logger, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val subscriptionRepository: SubscriptionRepository, - private val notificationsRepository: NotificationsRepository, -) { - - private val _events: MutableSharedFlow> = MutableSharedFlow() - val events: SharedFlow> = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.GetNotificationsParams) = supervisorScope { - val resultEvent = try { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - val auth = (response.result as ChatNotifyResponseAuthParams.Auth).auth - val responseJwtClaim = extractVerifiedDidJwtClaims(auth).getOrThrow() - responseJwtClaim.throwIfBaseIsInvalid() - - val metadata: AppMetaData = metadataStorageRepository.getByTopicAndType(wcResponse.topic, AppMetaDataType.PEER) - ?: throw IllegalStateException("No metadata found for topic ${wcResponse.topic}") - - val notifications = responseJwtClaim.notifications.mapIndexed { index, notification -> - with(notification) { - Notification( - id = id, topic = wcResponse.topic.value, sentAt = sentAt, metadata = metadata, - notificationMessage = NotificationMessage(title = convertToUTF8(title), body = convertToUTF8(body), icon = icon, url = url, type = type), - ) - } - } - - notificationsRepository.insertOrReplaceNotifications(notifications) - if (!responseJwtClaim.hasMore) subscriptionRepository.updateActiveSubscriptionWithLastNotificationId(notifications.lastOrNull()?.id, wcResponse.topic.value) - GetNotificationHistory.Success(notifications, responseJwtClaim.hasMore) - } - - is JsonRpcResponse.JsonRpcError -> GetNotificationHistory.Error(Throwable(response.error.message)) - } - } catch (e: Exception) { - logger.error(e) - GetNotificationHistory.Error(e) - } - - _events.emit(params to resultEvent) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnSubscribeResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnSubscribeResponseUseCase.kt deleted file mode 100644 index 48dd02063..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnSubscribeResponseUseCase.kt +++ /dev/null @@ -1,72 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.notify.common.model.CreateSubscription -import com.walletconnect.notify.data.jwt.subscription.SubscriptionRequestJwtClaim -import com.walletconnect.notify.data.jwt.subscription.SubscriptionResponseJwtClaim -import com.walletconnect.notify.data.storage.SubscriptionRepository -import com.walletconnect.notify.engine.domain.FindRequestedSubscriptionUseCase -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSubscribeResponseUseCase( - private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, - private val findRequestedSubscriptionUseCase: FindRequestedSubscriptionUseCase, - private val subscriptionRepository: SubscriptionRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val logger: Logger, -) { - private val _events: MutableSharedFlow> = MutableSharedFlow() - val events: SharedFlow> = _events.asSharedFlow() - - - suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.SubscribeParams) = supervisorScope { - jsonRpcInteractor.unsubscribe(wcResponse.topic) - - val resultEvent = try { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth - val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() - responseJwtClaim.throwIfBaseIsInvalid() - - val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() - val requestJwtClaim = extractVerifiedDidJwtClaims(params.subscriptionAuth).getOrThrow() - val subscription = findRequestedSubscriptionUseCase(requestJwtClaim.audience, subscriptions) - - CreateSubscription.Success(subscription) - } - - is JsonRpcResponse.JsonRpcError -> { - removeOptimisticallyAddedSubscription(wcResponse.topic) - CreateSubscription.Error(Throwable(response.error.message)) - } - - } - } catch (e: Exception) { - removeOptimisticallyAddedSubscription(wcResponse.topic) - logger.error(e) - CreateSubscription.Error(e) - } - - _events.emit(params to resultEvent) - } - - - private suspend fun removeOptimisticallyAddedSubscription(topic: Topic) = - runCatching { subscriptionRepository.deleteSubscriptionByNotifyTopic(topic.value) }.getOrElse { logger.error("OnSubscribeResponse - Error - No subscription found for removal") } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnUpdateResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnUpdateResponseUseCase.kt deleted file mode 100644 index 74bc0e62f..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnUpdateResponseUseCase.kt +++ /dev/null @@ -1,57 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.params.CoreNotifyParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.notify.common.model.CreateSubscription -import com.walletconnect.notify.common.model.UpdateSubscription -import com.walletconnect.notify.data.jwt.update.UpdateRequestJwtClaim -import com.walletconnect.notify.data.jwt.update.UpdateResponseJwtClaim -import com.walletconnect.notify.engine.domain.FindRequestedSubscriptionUseCase -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnUpdateResponseUseCase( - private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, - private val findRequestedSubscriptionUseCase: FindRequestedSubscriptionUseCase, - private val logger: Logger -) { - private val _events: MutableSharedFlow> = MutableSharedFlow() - val events: SharedFlow> = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, params: CoreNotifyParams.UpdateParams) = supervisorScope { - val resultEvent = try { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth - val responseJwtClaim = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() - responseJwtClaim.throwIfBaseIsInvalid() - - val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(responseJwtClaim.subject), responseJwtClaim.subscriptions).getOrThrow() - val requestJwtClaim = extractVerifiedDidJwtClaims(params.updateAuth).getOrThrow() - val subscription = findRequestedSubscriptionUseCase(requestJwtClaim.audience, subscriptions) - - UpdateSubscription.Success(subscription) - } - - is JsonRpcResponse.JsonRpcError -> UpdateSubscription.Error(Throwable(response.error.message)) - } - } catch (e: Exception) { - logger.error(e) - CreateSubscription.Error(e) - } - - _events.emit(params to resultEvent) - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt b/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt deleted file mode 100644 index 27c85faae..000000000 --- a/protocol/notify/src/main/kotlin/com/walletconnect/notify/engine/responses/OnWatchSubscriptionsResponseUseCase.kt +++ /dev/null @@ -1,81 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.notify.engine.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.jwt.did.extractVerifiedDidJwtClaims -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.ChatNotifyResponseAuthParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.foundation.util.Logger -import com.walletconnect.foundation.util.jwt.decodeDidPkh -import com.walletconnect.foundation.util.jwt.decodeEd25519DidKey -import com.walletconnect.notify.common.NotifyServerUrl -import com.walletconnect.notify.common.model.SubscriptionChanged -import com.walletconnect.notify.data.jwt.watchSubscriptions.WatchSubscriptionsResponseJwtClaim -import com.walletconnect.notify.data.storage.RegisteredAccountsRepository -import com.walletconnect.notify.engine.domain.ExtractPublicKeysFromDidJsonUseCase -import com.walletconnect.notify.engine.domain.SetActiveSubscriptionsUseCase -import com.walletconnect.notify.engine.domain.WatchSubscriptionsForEveryRegisteredAccountUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnWatchSubscriptionsResponseUseCase( - private val setActiveSubscriptionsUseCase: SetActiveSubscriptionsUseCase, - private val extractPublicKeysFromDidJsonUseCase: ExtractPublicKeysFromDidJsonUseCase, - private val watchSubscriptionsForEveryRegisteredAccountUseCase: WatchSubscriptionsForEveryRegisteredAccountUseCase, - private val accountsRepository: RegisteredAccountsRepository, - private val notifyServerUrl: NotifyServerUrl, - private val logger: Logger, -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse) = supervisorScope { - val resultEvent = try { - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - val responseAuth = (response.result as ChatNotifyResponseAuthParams.ResponseAuth).responseAuth - val jwtClaims = extractVerifiedDidJwtClaims(responseAuth).getOrThrow() - - jwtClaims.throwIfIsInvalid() - - val subscriptions = setActiveSubscriptionsUseCase(decodeDidPkh(jwtClaims.subject), jwtClaims.subscriptions).getOrThrow() - SubscriptionChanged(subscriptions) - } - - is JsonRpcResponse.JsonRpcError -> { - SDKError(Exception(response.errorMessage)) - } - } - } catch (e: Exception) { - logger.error(e) - SDKError(e) - } - - _events.emit(resultEvent) - } - - private suspend fun WatchSubscriptionsResponseJwtClaim.throwIfIsInvalid() { - throwIfBaseIsInvalid() - throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer() - } - - private suspend fun WatchSubscriptionsResponseJwtClaim.throwIfAudienceAndIssuerIsInvalidAndRetriggerWatchingLogicOnOutdatedIssuer() { - val expectedIssuer = runCatching { accountsRepository.getAccountByIdentityKey(decodeEd25519DidKey(audience).keyAsHex).notifyServerAuthenticationKey } - .getOrElse { throw IllegalStateException("Account does not exist in storage for $audience") } ?: throw IllegalStateException("Cached authentication public key is null") - - val decodedIssuerAsHex = decodeEd25519DidKey(issuer).keyAsHex - if (decodedIssuerAsHex != expectedIssuer.keyAsHex) { - val (_, newAuthenticationPublicKey) = extractPublicKeysFromDidJsonUseCase(notifyServerUrl.toUri()).getOrThrow() - - if (decodedIssuerAsHex == newAuthenticationPublicKey.keyAsHex) - watchSubscriptionsForEveryRegisteredAccountUseCase() - else - throw IllegalStateException("Issuer $decodedIssuerAsHex is not valid with cached ${expectedIssuer.keyAsHex} or fresh ${newAuthenticationPublicKey.keyAsHex}") - } - } -} \ No newline at end of file diff --git a/protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/ActiveSubscriptions.sq b/protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/ActiveSubscriptions.sq similarity index 100% rename from protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/ActiveSubscriptions.sq rename to protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/ActiveSubscriptions.sq diff --git a/protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/Notifications.sq b/protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/Notifications.sq similarity index 100% rename from protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/Notifications.sq rename to protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/Notifications.sq diff --git a/protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/RegisteredAccounts.sq b/protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/RegisteredAccounts.sq similarity index 100% rename from protocol/notify/src/main/sqldelight/com/walletconnect/notify/common/storage/data/dao/RegisteredAccounts.sq rename to protocol/notify/src/main/sqldelight/com/reown/notify/common/storage/data/dao/RegisteredAccounts.sq diff --git a/protocol/sign/ReadMe.md b/protocol/sign/ReadMe.md index f4c319ed1..464903ec5 100644 --- a/protocol/sign/ReadMe.md +++ b/protocol/sign/ReadMe.md @@ -36,9 +36,9 @@ allprojects { app/build.gradle.kts ```gradle -implementation(platform("com.walletconnect:android-bom:{BOM version}")) -implementation("com.walletconnect:android-core") -implementation("com.walletconnect:sign") +implementation(platform("com.reown:android-bom:{BOM version}")) +implementation("com.reown:android-core") +implementation("com.reown:sign") ```   diff --git a/protocol/sign/build.gradle.kts b/protocol/sign/build.gradle.kts index 78180891a..3ec02cab1 100644 --- a/protocol/sign/build.gradle.kts +++ b/protocol/sign/build.gradle.kts @@ -14,7 +14,7 @@ project.apply { } android { - namespace = "com.walletconnect.sign" + namespace = "com.reown.sign" compileSdk = COMPILE_SDK defaultConfig { @@ -89,7 +89,7 @@ android { sqldelight { databases { create("SignDatabase") { - packageName.set("com.walletconnect.sign") + packageName.set("com.reown.sign") schemaOutputDirectory.set(file("src/main/sqldelight/databases")) // generateAsync.set(true) // TODO: Enable once all repository methods have been converted to suspend functions verifyMigrations.set(true) @@ -100,7 +100,7 @@ sqldelight { dependencies { debugImplementation(project(":core:android")) - releaseImplementation("com.walletconnect:android-core:$CORE_VERSION") + releaseImplementation("com.reown:android-core:$CORE_VERSION") ksp(libs.moshi.ksp) implementation(libs.bundles.sqlDelight) diff --git a/protocol/sign/src/androidTest/AndroidManifest.xml b/protocol/sign/src/androidTest/AndroidManifest.xml index cb6dd1dbc..ccb36ba68 100644 --- a/protocol/sign/src/androidTest/AndroidManifest.xml +++ b/protocol/sign/src/androidTest/AndroidManifest.xml @@ -7,7 +7,7 @@ diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt new file mode 100644 index 000000000..d781b64a8 --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt @@ -0,0 +1,40 @@ +package com.reown.sign.di + +import com.reown.android.di.coreStorageModule +import com.reown.android.internal.common.di.coreAndroidNetworkModule +import com.reown.android.internal.common.di.coreCryptoModule +import com.reown.android.internal.common.di.coreJsonRpcModule +import com.reown.android.internal.common.di.corePairingModule +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayConnectionInterface +import org.koin.dsl.module + +private const val SHARED_PREFS_FILE = "wc_key_store" +private const val KEY_STORE_ALIAS = "wc_keystore_key" + +// When called more that once, different `storagePrefix` must be defined. +@JvmSynthetic +internal fun overrideModule( + relay: RelayConnectionInterface, + pairing: PairingInterface, + pairingController: PairingControllerInterface, + storagePrefix: String, + relayUrl: String, + connectionType: ConnectionType, + bundleId: String +) = module { + val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE + val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS + + single { relay } + + includes( + coreStorageModule(storagePrefix, bundleId), + corePairingModule(pairing, pairingController), + coreCryptoModule(sharedPrefsFile, keyStoreAlias), + coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId), + coreJsonRpcModule() + ) +} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/CacaoUtilsTests.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/CacaoUtilsTests.kt similarity index 91% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/CacaoUtilsTests.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/CacaoUtilsTests.kt index 1bdf4e112..426357f66 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/CacaoUtilsTests.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/CacaoUtilsTests.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.test +package com.reown.sign.test -import com.walletconnect.android.internal.common.signing.cacao.decodeReCaps -import com.walletconnect.android.internal.common.signing.cacao.getChains -import com.walletconnect.android.internal.common.signing.cacao.getMethods -import com.walletconnect.android.internal.common.signing.cacao.parseReCaps +import com.reown.android.internal.common.signing.cacao.decodeReCaps +import com.reown.android.internal.common.signing.cacao.getChains +import com.reown.android.internal.common.signing.cacao.getMethods +import com.reown.android.internal.common.signing.cacao.parseReCaps import org.junit.Test class CacaoUtilsTests { diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/GenerateAuthPayloadParamsTest.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/GenerateAuthPayloadParamsTest.kt similarity index 96% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/GenerateAuthPayloadParamsTest.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/GenerateAuthPayloadParamsTest.kt index 77d7afd1e..372df1528 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/GenerateAuthPayloadParamsTest.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/GenerateAuthPayloadParamsTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.test +package com.reown.sign.test -import com.walletconnect.android.internal.common.signing.cacao.getChains -import com.walletconnect.android.internal.common.signing.cacao.getMethods -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.utils.generateAuthPayloadParams +import com.reown.android.internal.common.signing.cacao.getChains +import com.reown.android.internal.common.signing.cacao.getMethods +import com.reown.sign.client.Sign +import com.reown.sign.client.utils.generateAuthPayloadParams import org.junit.Test class GenerateAuthPayloadParamsTest { diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/ParseReCapsTest.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/ParseReCapsTest.kt similarity index 92% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/ParseReCapsTest.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/ParseReCapsTest.kt index 0b3f4510d..e5ff9b5d4 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/ParseReCapsTest.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/ParseReCapsTest.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sign.test +package com.reown.sign.test -import com.walletconnect.android.internal.common.signing.cacao.getChains -import com.walletconnect.android.internal.common.signing.cacao.getMethods +import com.reown.android.internal.common.signing.cacao.getChains +import com.reown.android.internal.common.signing.cacao.getMethods import org.bouncycastle.util.encoders.Base64 import org.junit.Test diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/activity/InstrumentedTestActivity.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/activity/InstrumentedTestActivity.kt new file mode 100644 index 000000000..584879ae0 --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/activity/InstrumentedTestActivity.kt @@ -0,0 +1,11 @@ +package com.reown.sign.test.activity + +import android.app.Activity +import android.os.Bundle + +class InstrumentedTestActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/HybridAppInstrumentedAndroidTest.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/HybridAppInstrumentedAndroidTest.kt similarity index 92% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/HybridAppInstrumentedAndroidTest.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/HybridAppInstrumentedAndroidTest.kt index 17188319b..b37715488 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/HybridAppInstrumentedAndroidTest.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/HybridAppInstrumentedAndroidTest.kt @@ -1,22 +1,22 @@ -package com.walletconnect.sign.test.client - -import com.walletconnect.android.Core -import com.walletconnect.sign.BuildConfig -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.test.scenario.HybridAppInstrumentedActivityScenario -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.dapp.DappDelegate -import com.walletconnect.sign.test.utils.dapp.DappSignClient -import com.walletconnect.sign.test.utils.dapp.dappClientConnect -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.hybrid.HybridAppDappDelegate -import com.walletconnect.sign.test.utils.hybrid.HybridAppWalletDelegate -import com.walletconnect.sign.test.utils.hybrid.HybridSignClient -import com.walletconnect.sign.test.utils.hybrid.hybridClientConnect -import com.walletconnect.sign.test.utils.sessionNamespaces -import com.walletconnect.sign.test.utils.wallet.WalletDelegate -import com.walletconnect.sign.test.utils.wallet.WalletSignClient +package com.reown.sign.test.client + +import com.reown.android.Core +import com.reown.sign.BuildConfig +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.test.scenario.HybridAppInstrumentedActivityScenario +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.dapp.DappDelegate +import com.reown.sign.test.utils.dapp.DappSignClient +import com.reown.sign.test.utils.dapp.dappClientConnect +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.hybrid.HybridAppDappDelegate +import com.reown.sign.test.utils.hybrid.HybridAppWalletDelegate +import com.reown.sign.test.utils.hybrid.HybridSignClient +import com.reown.sign.test.utils.hybrid.hybridClientConnect +import com.reown.sign.test.utils.sessionNamespaces +import com.reown.sign.test.utils.wallet.WalletDelegate +import com.reown.sign.test.utils.wallet.WalletSignClient import junit.framework.TestCase import org.junit.Rule import org.junit.Test diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt similarity index 89% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt index ef7fb710e..b53c8a241 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SessionAuthenticateInstrumentedAndroidTest.kt @@ -1,26 +1,26 @@ -package com.walletconnect.sign.test.client - -import com.walletconnect.android.Core -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.utils.cacao.signHex -import com.walletconnect.sign.BuildConfig -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.client.utils.CacaoSigner -import com.walletconnect.sign.client.utils.generateAuthObject -import com.walletconnect.sign.client.utils.generateAuthPayloadParams -import com.walletconnect.sign.test.scenario.SignClientInstrumentedActivityScenario -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.dapp.DappDelegate -import com.walletconnect.sign.test.utils.dapp.DappSignClient -import com.walletconnect.sign.test.utils.dapp.dappClientAuthenticate -import com.walletconnect.sign.test.utils.dapp.dappClientAuthenticateLinkMode -import com.walletconnect.sign.test.utils.dapp.dappClientSendRequest -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.wallet.WalletDelegate -import com.walletconnect.sign.test.utils.wallet.WalletSignClient -import com.walletconnect.sign.test.utils.wallet.walletClientRespondToRequest -import com.walletconnect.util.hexToBytes +package com.reown.sign.test.client + +import com.reown.android.Core +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.utils.cacao.signHex +import com.reown.sign.BuildConfig +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.client.utils.CacaoSigner +import com.reown.sign.client.utils.generateAuthObject +import com.reown.sign.client.utils.generateAuthPayloadParams +import com.reown.sign.test.scenario.SignClientInstrumentedActivityScenario +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.dapp.DappDelegate +import com.reown.sign.test.utils.dapp.DappSignClient +import com.reown.sign.test.utils.dapp.dappClientAuthenticate +import com.reown.sign.test.utils.dapp.dappClientAuthenticateLinkMode +import com.reown.sign.test.utils.dapp.dappClientSendRequest +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.wallet.WalletDelegate +import com.reown.sign.test.utils.wallet.WalletSignClient +import com.reown.sign.test.utils.wallet.walletClientRespondToRequest +import com.reown.util.hexToBytes import org.junit.Rule import org.junit.Test import org.web3j.utils.Numeric diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SignClientInstrumentedAndroidTest.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SignClientInstrumentedAndroidTest.kt similarity index 89% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SignClientInstrumentedAndroidTest.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SignClientInstrumentedAndroidTest.kt index 3d4c77989..6b7781663 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/client/SignClientInstrumentedAndroidTest.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/client/SignClientInstrumentedAndroidTest.kt @@ -1,29 +1,29 @@ -package com.walletconnect.sign.test.client - -import com.walletconnect.android.Core -import com.walletconnect.sign.BuildConfig -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.test.scenario.SignClientInstrumentedActivityScenario -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.dapp.AutoApproveDappDelegate -import com.walletconnect.sign.test.utils.dapp.DappDelegate -import com.walletconnect.sign.test.utils.dapp.DappSignClient -import com.walletconnect.sign.test.utils.dapp.dappClientConnect -import com.walletconnect.sign.test.utils.dapp.dappClientSendRequest -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.sessionChains -import com.walletconnect.sign.test.utils.sessionEvents -import com.walletconnect.sign.test.utils.sessionNamespaceKey -import com.walletconnect.sign.test.utils.wallet.AutoApproveSessionWalletDelegate -import com.walletconnect.sign.test.utils.wallet.WalletDelegate -import com.walletconnect.sign.test.utils.wallet.WalletSignClient -import com.walletconnect.sign.test.utils.wallet.dappClientExtendSession -import com.walletconnect.sign.test.utils.wallet.rejectOnSessionProposal -import com.walletconnect.sign.test.utils.wallet.walletClientEmitEvent -import com.walletconnect.sign.test.utils.wallet.walletClientExtendSession -import com.walletconnect.sign.test.utils.wallet.walletClientRespondToRequest -import com.walletconnect.sign.test.utils.wallet.walletClientUpdateSession +package com.reown.sign.test.client + +import com.reown.android.Core +import com.reown.sign.BuildConfig +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.test.scenario.SignClientInstrumentedActivityScenario +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.dapp.AutoApproveDappDelegate +import com.reown.sign.test.utils.dapp.DappDelegate +import com.reown.sign.test.utils.dapp.DappSignClient +import com.reown.sign.test.utils.dapp.dappClientConnect +import com.reown.sign.test.utils.dapp.dappClientSendRequest +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.sessionChains +import com.reown.sign.test.utils.sessionEvents +import com.reown.sign.test.utils.sessionNamespaceKey +import com.reown.sign.test.utils.wallet.AutoApproveSessionWalletDelegate +import com.reown.sign.test.utils.wallet.WalletDelegate +import com.reown.sign.test.utils.wallet.WalletSignClient +import com.reown.sign.test.utils.wallet.dappClientExtendSession +import com.reown.sign.test.utils.wallet.rejectOnSessionProposal +import com.reown.sign.test.utils.wallet.walletClientEmitEvent +import com.reown.sign.test.utils.wallet.walletClientExtendSession +import com.reown.sign.test.utils.wallet.walletClientRespondToRequest +import com.reown.sign.test.utils.wallet.walletClientUpdateSession import junit.framework.TestCase.fail import org.junit.Rule import org.junit.Test diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt similarity index 92% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt index 6e2fc192e..e3eba3525 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/HybridAppInstrumentedActivityScenario.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.test.scenario +package com.reown.sign.test.scenario -import com.walletconnect.android.internal.common.scope -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.sign.test.BuildConfig -import com.walletconnect.sign.test.utils.TestClient +import com.reown.android.internal.common.scope +import com.reown.foundation.network.model.Relay +import com.reown.sign.test.BuildConfig +import com.reown.sign.test.utils.TestClient import junit.framework.TestCase import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignActivityScenario.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignActivityScenario.kt similarity index 93% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignActivityScenario.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignActivityScenario.kt index a70bda0a1..121f2a960 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignActivityScenario.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignActivityScenario.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sign.test.scenario +package com.reown.sign.test.scenario import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario -import com.walletconnect.sign.test.activity.InstrumentedTestActivity +import com.reown.sign.test.activity.InstrumentedTestActivity import junit.framework.TestCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignClientInstrumentedActivityScenario.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignClientInstrumentedActivityScenario.kt similarity index 91% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignClientInstrumentedActivityScenario.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignClientInstrumentedActivityScenario.kt index 611273cec..2bc5d96e8 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/scenario/SignClientInstrumentedActivityScenario.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/scenario/SignClientInstrumentedActivityScenario.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.test.scenario +package com.reown.sign.test.scenario -import com.walletconnect.android.internal.common.scope -import com.walletconnect.foundation.network.model.Relay -import com.walletconnect.sign.test.BuildConfig -import com.walletconnect.sign.test.utils.TestClient +import com.reown.android.internal.common.scope +import com.reown.foundation.network.model.Relay +import com.reown.sign.test.BuildConfig +import com.reown.sign.test.utils.TestClient import junit.framework.TestCase.fail import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/Common.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/Common.kt new file mode 100644 index 000000000..bbb97e86e --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/Common.kt @@ -0,0 +1,29 @@ +package com.reown.sign.test.utils + +import com.reown.android.Core +import com.reown.sign.client.Sign +import junit.framework.TestCase.fail +import timber.log.Timber + +internal fun globalOnError(error: Sign.Model.Error) { + Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") + fail(error.throwable.message) +} + +internal fun globalOnError(error: Core.Model.Error) { + Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") + fail(error.throwable.message) +} + + +const val sessionNamespaceKey = "eip155" +val sessionChains = listOf("eip155:1") +val sessionAccounts = listOf("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb") +val sessionMethods = listOf("personal_sign") +val sessionEvents = listOf("someEvent") + +val sessionNamespace = Sign.Model.Namespace.Session(sessionChains, sessionAccounts, sessionMethods, sessionEvents) +val proposalNamespace = Sign.Model.Namespace.Proposal(sessionChains, sessionMethods, sessionEvents) + +val proposalNamespaces: Map = mapOf(sessionNamespaceKey to proposalNamespace) +val sessionNamespaces: Map = mapOf(sessionNamespaceKey to sessionNamespace) \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/TestClient.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/TestClient.kt new file mode 100644 index 000000000..2e16e7970 --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/TestClient.kt @@ -0,0 +1,241 @@ +package com.reown.sign.test.utils + +import android.app.Application +import androidx.test.core.app.ApplicationProvider +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.CoreProtocol +import com.reown.android.relay.ConnectionType +import com.reown.android.relay.RelayClient +import com.reown.sign.BuildConfig +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.client.SignProtocol +import com.reown.sign.di.overrideModule +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import org.koin.core.KoinApplication +import timber.log.Timber + +internal object TestClient { + private val app = ApplicationProvider.getApplicationContext() + fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = init().apply { createEagerInstances() } + + object Wallet { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Wallet", + description = "Wallet for automation tests", + url = "kotlin.e2e.wallet", + icons = listOf(), + redirect = null + ) + + private val coreProtocol = CoreClient.apply { + Timber.d("Wallet CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) + Relay.connect(::globalOnError) + } + + private val initParams = Sign.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + internal val signClient = SignClient.apply { + initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) + Timber.d("Wallet CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val Pairing = coreProtocol.Pairing + } + + object WalletLinkMode { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Wallet", + description = "Wallet for automation tests", + url = "kotlin.e2e.wallet", + icons = listOf(), + appLink = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet", + linkMode = true, + redirect = null + ) + + private val coreProtocol = CoreClient.apply { + Timber.d("Wallet CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) + Relay.connect(::globalOnError) + } + + private val initParams = Sign.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + internal val signClient = SignClient.apply { + initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) + Timber.d("Wallet CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val Pairing = coreProtocol.Pairing + } + + object Dapp { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Dapp", + description = "Dapp for automation tests", + url = "kotlin.e2e.dapp", + icons = listOf(), + redirect = null + ) + + private val dappKoinApp = KoinApplication.createNewWCKoinApp() + + private val coreProtocol = CoreProtocol(dappKoinApp).apply { + Timber.d("Dapp CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } + + // Override of previous Relay necessary for reinitialization of `eventsFlow` + Relay = RelayClient(dappKoinApp) + + // Override of storage instances and depending objects + dappKoinApp.modules( + overrideModule( + Relay, + Pairing, + PairingController, + "test_dapp", + "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", + ConnectionType.MANUAL, + app.packageName + ) + ) + + // Necessary reinit of Relay, Pairing and PairingController + Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } + Pairing.initialize() + PairingController.initialize() + + Relay.connect(::globalOnError) + } + + private val initParams = Sign.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + internal val signClient = SignProtocol(dappKoinApp).apply { + initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) + Timber.d("Dapp CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val Pairing = coreProtocol.Pairing + } + + object DappLinkMode { + + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Dapp", + description = "Dapp for automation tests", + url = "kotlin.e2e.dapp", + icons = listOf(), + linkMode = true, + appLink = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/dapp", + redirect = null + ) + + private val dappKoinApp = KoinApplication.createNewWCKoinApp() + + private val coreProtocol = CoreProtocol(dappKoinApp).apply { + Timber.d("Dapp CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } + + // Override of previous Relay necessary for reinitialization of `eventsFlow` + Relay = RelayClient(dappKoinApp) + + // Override of storage instances and depending objects + dappKoinApp.modules( + overrideModule( + Relay, + Pairing, + PairingController, + "test_hybrid", + "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", + ConnectionType.MANUAL, + app.packageName + ) + ) + + // Necessary reinit of Relay, Pairing and PairingController + Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } + Pairing.initialize() + PairingController.initialize() + + Relay.connect(::globalOnError) + } + + private val initParams = Sign.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + internal val signClientLinkMode = SignProtocol(dappKoinApp).apply { + initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) + Timber.d("Dapp CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val Pairing = coreProtocol.Pairing + } + + object Hybrid { + private val metadata = Core.Model.AppMetaData( + name = "Kotlin E2E Hybrid App", + description = "Hybrid App for automation tests", + url = "kotlin.e2e.hybrid", + icons = listOf(), + redirect = null + ) + + private val hybridKoinApp = KoinApplication.createNewWCKoinApp() + + private val coreProtocol = CoreProtocol(hybridKoinApp).apply { + Timber.d("Hybrid CP start: ") + initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } + + // Override of previous Relay necessary for reinitialization of `eventsFlow` + Relay = RelayClient(hybridKoinApp) + + // Override of storage instances and depending objects + hybridKoinApp.modules( + overrideModule( + Relay, + Pairing, + PairingController, + "test_hybrid", + "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", + ConnectionType.MANUAL, + app.packageName + ) + ) + + // Necessary reinit of Relay, Pairing and PairingController + Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } + Pairing.initialize() + PairingController.initialize() + + Relay.connect(::globalOnError) + } + + private val initParams = Sign.Params.Init(coreProtocol) + private var _isInitialized = MutableStateFlow(false) + internal var isInitialized = _isInitialized.asStateFlow() + + internal val signClient = SignProtocol(hybridKoinApp) + .apply { + initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) + initialize(initParams, onSuccess = { println("Second Init") }, onError = { Timber.e("Second Init Error: ${it.throwable}") }) + Timber.d("Hybrid CP finish: ") + } + + internal val Relay get() = coreProtocol.Relay + internal val Pairing = coreProtocol.Pairing + } +} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappDelegate.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappDelegate.kt new file mode 100644 index 000000000..13ffbd685 --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappDelegate.kt @@ -0,0 +1,51 @@ +package com.reown.sign.test.utils.dapp + +import com.reown.sign.BuildConfig +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.test.utils.globalOnError +import junit.framework.TestCase.fail +import timber.log.Timber +import kotlin.time.Duration.Companion.seconds + +open class DappDelegate : SignClient.DappDelegate { + override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) {} + override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) {} + override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) {} + override fun onSessionEvent(sessionEvent: Sign.Model.Event) {} + override fun onSessionExtend(session: Sign.Model.Session) {} + override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) {} + override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) {} + override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) {} + override fun onRequestExpired(request: Sign.Model.ExpiredRequest) {} + override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {} + override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) {} + override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { + Timber.d("Dapp: onConnectionStateChange: $state") + } + + override fun onError(error: Sign.Model.Error) { + globalOnError(error) + } +} + +open class AutoApproveDappDelegate(val onSessionApprovedSuccess: (approvedSession: Sign.Model.ApprovedSession) -> Unit) : DappDelegate() { + override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) { + approvedSession.onSessionApproved { onSessionApprovedSuccess(approvedSession) } + } +} + +fun Sign.Model.ApprovedSession.onSessionApproved(onSuccess: () -> Unit) { + Timber.d("dappDelegate: onSessionApproved") + + DappSignClient.ping(Sign.Params.Ping(topic, BuildConfig.TEST_TIMEOUT_SECONDS.seconds), object : Sign.Listeners.SessionPing { + override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { + Timber.d("dappDelegate: onPingSuccess") + onSuccess() + } + + override fun onError(pingError: Sign.Model.Ping.Error) { + fail(pingError.error.message) + } + }) +} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappSignClient.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappSignClient.kt similarity index 83% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappSignClient.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappSignClient.kt index b388c079e..c2d7147e6 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappSignClient.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/dapp/DappSignClient.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sign.test.utils.dapp +package com.reown.sign.test.utils.dapp -import com.walletconnect.android.Core -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.proposalNamespaces -import com.walletconnect.sign.test.utils.sessionChains -import com.walletconnect.sign.test.utils.sessionMethods -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes +import com.reown.android.Core +import com.reown.sign.client.Sign +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.proposalNamespaces +import com.reown.sign.test.utils.sessionChains +import com.reown.sign.test.utils.sessionMethods +import com.reown.util.bytesToHex +import com.reown.util.randomBytes import timber.log.Timber val DappSignClient = TestClient.Dapp.signClient diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridDelegate.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridDelegate.kt similarity index 92% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridDelegate.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridDelegate.kt index 88422190a..b26f80496 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridDelegate.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridDelegate.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sign.test.utils.hybrid +package com.reown.sign.test.utils.hybrid -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.test.utils.globalOnError +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.test.utils.globalOnError import timber.log.Timber open class HybridAppWalletDelegate : SignClient.WalletDelegate { diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridSignClient.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridSignClient.kt new file mode 100644 index 000000000..ac076a7f7 --- /dev/null +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/hybrid/HybridSignClient.kt @@ -0,0 +1,19 @@ +package com.reown.sign.test.utils.hybrid + +import com.reown.android.Core +import com.reown.sign.client.Sign +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.proposalNamespaces +import timber.log.Timber + +val HybridSignClient = TestClient.Hybrid.signClient + +val hybridClientConnect = { pairing: Core.Model.Pairing -> + val connectParams = Sign.Params.Connect(namespaces = proposalNamespaces, optionalNamespaces = null, properties = null, pairing = pairing) + HybridSignClient.connect( + connectParams, + onSuccess = { url -> Timber.d("HybridDappClient: connect onSuccess, url: $url") }, + onError = ::globalOnError + ) +} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletDelegate.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletDelegate.kt similarity index 89% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletDelegate.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletDelegate.kt index 08b4dbf55..68d7625e9 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletDelegate.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletDelegate.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.test.utils.wallet +package com.reown.sign.test.utils.wallet -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.sessionNamespaces +import com.reown.sign.client.Sign +import com.reown.sign.client.SignClient +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.sessionNamespaces import timber.log.Timber open class WalletDelegate : SignClient.WalletDelegate { diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletSignClient.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletSignClient.kt similarity index 75% rename from protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletSignClient.kt rename to protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletSignClient.kt index 1722b33e8..f42a87813 100644 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/wallet/WalletSignClient.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/test/utils/wallet/WalletSignClient.kt @@ -1,13 +1,13 @@ -package com.walletconnect.sign.test.utils.wallet - -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.dapp.DappSignClient -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.sessionChains -import com.walletconnect.sign.test.utils.sessionEvents -import com.walletconnect.sign.test.utils.sessionNamespace -import com.walletconnect.sign.test.utils.sessionNamespaceKey +package com.reown.sign.test.utils.wallet + +import com.reown.sign.client.Sign +import com.reown.sign.test.utils.TestClient +import com.reown.sign.test.utils.dapp.DappSignClient +import com.reown.sign.test.utils.globalOnError +import com.reown.sign.test.utils.sessionChains +import com.reown.sign.test.utils.sessionEvents +import com.reown.sign.test.utils.sessionNamespace +import com.reown.sign.test.utils.sessionNamespaceKey import timber.log.Timber val WalletSignClient = TestClient.Wallet.signClient diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/di/OverrideModule.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/di/OverrideModule.kt deleted file mode 100644 index c41335440..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/di/OverrideModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.walletconnect.sign.di - -import com.walletconnect.android.di.coreStorageModule -import com.walletconnect.android.internal.common.di.coreAndroidNetworkModule -import com.walletconnect.android.internal.common.di.coreCryptoModule -import com.walletconnect.android.internal.common.di.coreJsonRpcModule -import com.walletconnect.android.internal.common.di.corePairingModule -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayConnectionInterface -import org.koin.dsl.module - -private const val SHARED_PREFS_FILE = "wc_key_store" -private const val KEY_STORE_ALIAS = "wc_keystore_key" - -// When called more that once, different `storagePrefix` must be defined. -@JvmSynthetic -internal fun overrideModule( - relay: RelayConnectionInterface, - pairing: PairingInterface, - pairingController: PairingControllerInterface, - storagePrefix: String, - relayUrl: String, - connectionType: ConnectionType, - bundleId: String -) = module { - val sharedPrefsFile = storagePrefix + SHARED_PREFS_FILE - val keyStoreAlias = storagePrefix + KEY_STORE_ALIAS - - single { relay } - - includes( - coreStorageModule(storagePrefix, bundleId), - corePairingModule(pairing, pairingController), - coreCryptoModule(sharedPrefsFile, keyStoreAlias), - coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId), - coreJsonRpcModule() - ) -} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/activity/InstrumentedTestActivity.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/activity/InstrumentedTestActivity.kt deleted file mode 100644 index 9aef5841d..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/activity/InstrumentedTestActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.sign.test.activity - -import android.app.Activity -import android.os.Bundle - -class InstrumentedTestActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } -} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/Common.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/Common.kt deleted file mode 100644 index f293f0479..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/Common.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.walletconnect.sign.test.utils - -import com.walletconnect.android.Core -import com.walletconnect.sign.client.Sign -import junit.framework.TestCase.fail -import timber.log.Timber - -internal fun globalOnError(error: Sign.Model.Error) { - Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") - fail(error.throwable.message) -} - -internal fun globalOnError(error: Core.Model.Error) { - Timber.e("globalOnError: ${error.throwable.stackTraceToString()}") - fail(error.throwable.message) -} - - -const val sessionNamespaceKey = "eip155" -val sessionChains = listOf("eip155:1") -val sessionAccounts = listOf("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb") -val sessionMethods = listOf("personal_sign") -val sessionEvents = listOf("someEvent") - -val sessionNamespace = Sign.Model.Namespace.Session(sessionChains, sessionAccounts, sessionMethods, sessionEvents) -val proposalNamespace = Sign.Model.Namespace.Proposal(sessionChains, sessionMethods, sessionEvents) - -val proposalNamespaces: Map = mapOf(sessionNamespaceKey to proposalNamespace) -val sessionNamespaces: Map = mapOf(sessionNamespaceKey to sessionNamespace) \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/TestClient.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/TestClient.kt deleted file mode 100644 index 44a913edf..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/TestClient.kt +++ /dev/null @@ -1,241 +0,0 @@ -package com.walletconnect.sign.test.utils - -import android.app.Application -import androidx.test.core.app.ApplicationProvider -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.CoreProtocol -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.android.relay.RelayClient -import com.walletconnect.sign.BuildConfig -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.client.SignProtocol -import com.walletconnect.sign.di.overrideModule -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import org.koin.core.KoinApplication -import timber.log.Timber - -internal object TestClient { - private val app = ApplicationProvider.getApplicationContext() - fun KoinApplication.Companion.createNewWCKoinApp(): KoinApplication = init().apply { createEagerInstances() } - - object Wallet { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Wallet", - description = "Wallet for automation tests", - url = "kotlin.e2e.wallet", - icons = listOf(), - redirect = null - ) - - private val coreProtocol = CoreClient.apply { - Timber.d("Wallet CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) - Relay.connect(::globalOnError) - } - - private val initParams = Sign.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - internal val signClient = SignClient.apply { - initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) - Timber.d("Wallet CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val Pairing = coreProtocol.Pairing - } - - object WalletLinkMode { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Wallet", - description = "Wallet for automation tests", - url = "kotlin.e2e.wallet", - icons = listOf(), - appLink = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet", - linkMode = true, - redirect = null - ) - - private val coreProtocol = CoreClient.apply { - Timber.d("Wallet CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL, onError = ::globalOnError) - Relay.connect(::globalOnError) - } - - private val initParams = Sign.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - internal val signClient = SignClient.apply { - initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) - Timber.d("Wallet CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val Pairing = coreProtocol.Pairing - } - - object Dapp { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Dapp", - description = "Dapp for automation tests", - url = "kotlin.e2e.dapp", - icons = listOf(), - redirect = null - ) - - private val dappKoinApp = KoinApplication.createNewWCKoinApp() - - private val coreProtocol = CoreProtocol(dappKoinApp).apply { - Timber.d("Dapp CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } - - // Override of previous Relay necessary for reinitialization of `eventsFlow` - Relay = RelayClient(dappKoinApp) - - // Override of storage instances and depending objects - dappKoinApp.modules( - overrideModule( - Relay, - Pairing, - PairingController, - "test_dapp", - "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", - ConnectionType.MANUAL, - app.packageName - ) - ) - - // Necessary reinit of Relay, Pairing and PairingController - Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } - Pairing.initialize() - PairingController.initialize() - - Relay.connect(::globalOnError) - } - - private val initParams = Sign.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - internal val signClient = SignProtocol(dappKoinApp).apply { - initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) - Timber.d("Dapp CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val Pairing = coreProtocol.Pairing - } - - object DappLinkMode { - - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Dapp", - description = "Dapp for automation tests", - url = "kotlin.e2e.dapp", - icons = listOf(), - linkMode = true, - appLink = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/dapp", - redirect = null - ) - - private val dappKoinApp = KoinApplication.createNewWCKoinApp() - - private val coreProtocol = CoreProtocol(dappKoinApp).apply { - Timber.d("Dapp CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } - - // Override of previous Relay necessary for reinitialization of `eventsFlow` - Relay = RelayClient(dappKoinApp) - - // Override of storage instances and depending objects - dappKoinApp.modules( - overrideModule( - Relay, - Pairing, - PairingController, - "test_hybrid", - "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", - ConnectionType.MANUAL, - app.packageName - ) - ) - - // Necessary reinit of Relay, Pairing and PairingController - Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } - Pairing.initialize() - PairingController.initialize() - - Relay.connect(::globalOnError) - } - - private val initParams = Sign.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - internal val signClientLinkMode = SignProtocol(dappKoinApp).apply { - initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) - Timber.d("Dapp CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val Pairing = coreProtocol.Pairing - } - - object Hybrid { - private val metadata = Core.Model.AppMetaData( - name = "Kotlin E2E Hybrid App", - description = "Hybrid App for automation tests", - url = "kotlin.e2e.hybrid", - icons = listOf(), - redirect = null - ) - - private val hybridKoinApp = KoinApplication.createNewWCKoinApp() - - private val coreProtocol = CoreProtocol(hybridKoinApp).apply { - Timber.d("Hybrid CP start: ") - initialize(app, BuildConfig.PROJECT_ID, metadata, ConnectionType.MANUAL) { Timber.e(it.throwable) } - - // Override of previous Relay necessary for reinitialization of `eventsFlow` - Relay = RelayClient(hybridKoinApp) - - // Override of storage instances and depending objects - hybridKoinApp.modules( - overrideModule( - Relay, - Pairing, - PairingController, - "test_hybrid", - "wss://relay.walletconnect.org?projectId=${BuildConfig.PROJECT_ID}", - ConnectionType.MANUAL, - app.packageName - ) - ) - - // Necessary reinit of Relay, Pairing and PairingController - Relay.initialize(ConnectionType.MANUAL) { Timber.e(it) } - Pairing.initialize() - PairingController.initialize() - - Relay.connect(::globalOnError) - } - - private val initParams = Sign.Params.Init(coreProtocol) - private var _isInitialized = MutableStateFlow(false) - internal var isInitialized = _isInitialized.asStateFlow() - - internal val signClient = SignProtocol(hybridKoinApp) - .apply { - initialize(initParams, onSuccess = { _isInitialized.tryEmit(true) }, onError = { Timber.e(it.throwable) }) - initialize(initParams, onSuccess = { println("Second Init") }, onError = { Timber.e("Second Init Error: ${it.throwable}") }) - Timber.d("Hybrid CP finish: ") - } - - internal val Relay get() = coreProtocol.Relay - internal val Pairing = coreProtocol.Pairing - } -} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappDelegate.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappDelegate.kt deleted file mode 100644 index 52969b5a9..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/dapp/DappDelegate.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.walletconnect.sign.test.utils.dapp - -import com.walletconnect.sign.BuildConfig -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.SignClient -import com.walletconnect.sign.test.utils.globalOnError -import junit.framework.TestCase.fail -import timber.log.Timber -import kotlin.time.Duration.Companion.seconds - -open class DappDelegate : SignClient.DappDelegate { - override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) {} - override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) {} - override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) {} - override fun onSessionEvent(sessionEvent: Sign.Model.Event) {} - override fun onSessionExtend(session: Sign.Model.Session) {} - override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) {} - override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) {} - override fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) {} - override fun onRequestExpired(request: Sign.Model.ExpiredRequest) {} - override fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {} - override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) {} - override fun onConnectionStateChange(state: Sign.Model.ConnectionState) { - Timber.d("Dapp: onConnectionStateChange: $state") - } - - override fun onError(error: Sign.Model.Error) { - globalOnError(error) - } -} - -open class AutoApproveDappDelegate(val onSessionApprovedSuccess: (approvedSession: Sign.Model.ApprovedSession) -> Unit) : DappDelegate() { - override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) { - approvedSession.onSessionApproved { onSessionApprovedSuccess(approvedSession) } - } -} - -fun Sign.Model.ApprovedSession.onSessionApproved(onSuccess: () -> Unit) { - Timber.d("dappDelegate: onSessionApproved") - - DappSignClient.ping(Sign.Params.Ping(topic, BuildConfig.TEST_TIMEOUT_SECONDS.seconds), object : Sign.Listeners.SessionPing { - override fun onSuccess(pingSuccess: Sign.Model.Ping.Success) { - Timber.d("dappDelegate: onPingSuccess") - onSuccess() - } - - override fun onError(pingError: Sign.Model.Ping.Error) { - fail(pingError.error.message) - } - }) -} \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridSignClient.kt b/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridSignClient.kt deleted file mode 100644 index 7969afb38..000000000 --- a/protocol/sign/src/androidTest/kotlin/com/walletconnect/sign/test/utils/hybrid/HybridSignClient.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.sign.test.utils.hybrid - -import com.walletconnect.android.Core -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.test.utils.TestClient -import com.walletconnect.sign.test.utils.globalOnError -import com.walletconnect.sign.test.utils.proposalNamespaces -import timber.log.Timber - -val HybridSignClient = TestClient.Hybrid.signClient - -val hybridClientConnect = { pairing: Core.Model.Pairing -> - val connectParams = Sign.Params.Connect(namespaces = proposalNamespaces, optionalNamespaces = null, properties = null, pairing = pairing) - HybridSignClient.connect( - connectParams, - onSuccess = { url -> Timber.d("HybridDappClient: connect onSuccess, url: $url") }, - onError = ::globalOnError - ) -} \ No newline at end of file diff --git a/protocol/sign/src/main/AndroidManifest.xml b/protocol/sign/src/main/AndroidManifest.xml index 3c2701c08..91067cbd5 100644 --- a/protocol/sign/src/main/AndroidManifest.xml +++ b/protocol/sign/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.reown.sign"> \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/Sign.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/Sign.kt similarity index 97% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/client/Sign.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/client/Sign.kt index 5a776b407..383f412fc 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/Sign.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/Sign.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sign.client +package com.reown.sign.client import androidx.annotation.Keep -import com.walletconnect.android.Core -import com.walletconnect.android.CoreInterface -import com.walletconnect.android.cacao.SignatureInterface -import com.walletconnect.android.internal.common.signing.cacao.Issuer +import com.reown.android.Core +import com.reown.android.CoreInterface +import com.reown.android.cacao.SignatureInterface +import com.reown.android.internal.common.signing.cacao.Issuer import java.net.URI import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -20,7 +20,7 @@ object Sign { @Deprecated( message = "ConnectionType for the relay is moved to CoreClient", - replaceWith = ReplaceWith(expression = "ConnectionType", imports = ["com.walletconnect.android.relay"]) + replaceWith = ReplaceWith(expression = "ConnectionType", imports = ["com.reown.android.relay"]) ) enum class ConnectionType { AUTOMATIC, MANUAL diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignClient.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignClient.kt similarity index 82% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignClient.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/client/SignClient.kt index 663f898d3..b91193d2e 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignClient.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignClient.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.client +package com.reown.sign.client object SignClient : SignInterface by SignProtocol.instance { interface WalletDelegate: SignInterface.WalletDelegate diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/client/SignInterface.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignInterface.kt new file mode 100644 index 000000000..2e61406bb --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignInterface.kt @@ -0,0 +1,120 @@ +package com.reown.sign.client + +interface SignInterface { + interface WalletDelegate { + fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal, verifyContext: Sign.Model.VerifyContext) + val onSessionAuthenticate: ((Sign.Model.SessionAuthenticate, Sign.Model.VerifyContext) -> Unit)? get() = null + fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest, verifyContext: Sign.Model.VerifyContext) + fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) + fun onSessionExtend(session: Sign.Model.Session) + + //Responses + fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) + fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) + + //Utils + fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { + //override me + } + + fun onRequestExpired(request: Sign.Model.ExpiredRequest) { + //override me + } + + fun onConnectionStateChange(state: Sign.Model.ConnectionState) + fun onError(error: Sign.Model.Error) + } + + interface DappDelegate { + fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) + fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) + fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) + + @Deprecated( + + message = "onSessionEvent is deprecated. Use onEvent instead. Using both will result in duplicate events.", + replaceWith = ReplaceWith(expression = "onEvent(event)") + ) + fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) + fun onSessionEvent(sessionEvent: Sign.Model.Event) {} + fun onSessionExtend(session: Sign.Model.Session) + fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) + + //Responses + fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) + fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {} + + // Utils + fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) + fun onRequestExpired(request: Sign.Model.ExpiredRequest) + fun onConnectionStateChange(state: Sign.Model.ConnectionState) + fun onError(error: Sign.Model.Error) + } + + fun initialize(init: Sign.Params.Init, onSuccess: () -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun setWalletDelegate(delegate: WalletDelegate) + fun setDappDelegate(delegate: DappDelegate) + fun connect( + connect: Sign.Params.Connect, onSuccess: (String) -> Unit, + onError: (Sign.Model.Error) -> Unit, + ) + + fun authenticate(authenticate: Sign.Params.Authenticate, walletAppLink: String? = null, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit) + fun dispatchEnvelope(urlWithEnvelope: String, onError: (Sign.Model.Error) -> Unit) + fun approveSession(approve: Sign.Params.Approve, onSuccess: (Sign.Params.Approve) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun rejectSession(reject: Sign.Params.Reject, onSuccess: (Sign.Params.Reject) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun approveAuthenticate(approve: Sign.Params.ApproveAuthenticate, onSuccess: (Sign.Params.ApproveAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) + fun rejectAuthenticate(reject: Sign.Params.RejectAuthenticate, onSuccess: (Sign.Params.RejectAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) + fun formatAuthMessage(formatMessage: Sign.Params.FormatMessage): String + + fun request(request: Sign.Params.Request, onSuccess: (Sign.Model.SentRequest) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun respond(response: Sign.Params.Response, onSuccess: (Sign.Params.Response) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun update(update: Sign.Params.Update, onSuccess: (Sign.Params.Update) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun extend(extend: Sign.Params.Extend, onSuccess: (Sign.Params.Extend) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun emit(emit: Sign.Params.Emit, onSuccess: (Sign.Params.Emit) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun ping(ping: Sign.Params.Ping, sessionPing: Sign.Listeners.SessionPing? = null) + fun disconnect(disconnect: Sign.Params.Disconnect, onSuccess: (Sign.Params.Disconnect) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) + fun decryptMessage(params: Sign.Params.DecryptMessage, onSuccess: (Sign.Model.Message) -> Unit, onError: (Sign.Model.Error) -> Unit) + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getListOfActiveSessions(): List + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getActiveSessionByTopic(topic: String): Sign.Model.Session? + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getPendingSessionRequests(topic: String): List + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getSessionProposals(): List + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getVerifyContext(id: Long): Sign.Model.VerifyContext? + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getPendingAuthenticateRequests(): List + + /** + * Caution: This function is blocking and runs on the current thread. + * It is advised that this function be called from background operation + */ + fun getListOfVerifyContexts(): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignProtocol.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignProtocol.kt similarity index 78% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignProtocol.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/client/SignProtocol.kt index ca5333dcb..516ec2a39 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignProtocol.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/SignProtocol.kt @@ -1,24 +1,24 @@ @file:JvmSynthetic -package com.walletconnect.sign.client - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.di.DatabaseConfig -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.pairing.model.mapper.toPairing -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.client.mapper.* -import com.walletconnect.sign.common.exceptions.SignClientAlreadyInitializedException -import com.walletconnect.sign.di.engineModule -import com.walletconnect.sign.di.signJsonRpcModule -import com.walletconnect.sign.di.storageModule -import com.walletconnect.sign.engine.domain.SignEngine -import com.walletconnect.sign.engine.model.EngineDO +package com.reown.sign.client + +import com.reown.android.Core +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.di.DatabaseConfig +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.pairing.model.mapper.toPairing +import com.reown.android.relay.WSSConnectionState +import com.reown.foundation.common.model.Topic +import com.reown.sign.client.mapper.* +import com.reown.sign.common.exceptions.SignClientAlreadyInitializedException +import com.reown.sign.di.engineModule +import com.reown.sign.di.signJsonRpcModule +import com.reown.sign.di.storageModule +import com.reown.sign.engine.domain.SignEngine +import com.reown.sign.engine.model.EngineDO import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -172,27 +172,6 @@ class SignProtocol(private val koinApp: KoinApplication = wcKoinApp) : SignInter return runBlocking { signEngine.formatMessage(formatMessage.payloadParams.toEngine(), formatMessage.iss) } } - @Throws(IllegalStateException::class) - override fun pair( - pair: Sign.Params.Pair, - onSuccess: (Sign.Params.Pair) -> Unit, - onError: (Sign.Model.Error) -> Unit, - ) { - checkEngineInitialization() - - scope.launch { - try { - signEngine.pair( - uri = pair.uri, - onSuccess = { onSuccess(pair) }, - onFailure = { throwable -> onError(Sign.Model.Error(throwable)) } - ) - } catch (error: Exception) { - onError(Sign.Model.Error(error)) - } - } - } - @Throws(IllegalStateException::class) override fun approveSession(approve: Sign.Params.Approve, onSuccess: (Sign.Params.Approve) -> Unit, onError: (Sign.Model.Error) -> Unit) { checkEngineInitialization() @@ -424,23 +403,6 @@ class SignProtocol(private val koinApp: KoinApplication = wcKoinApp) : SignInter } } - @Throws(IllegalStateException::class) - override fun getListOfSettledSessions(): List { - checkEngineInitialization() - return runBlocking { - signEngine.getListOfSettledSessions().map(EngineDO.Session::toClientActiveSession) - } - } - - @Throws(IllegalStateException::class) - override fun getSettledSessionByTopic(topic: String): Sign.Model.Session? { - checkEngineInitialization() - return runBlocking { - signEngine.getListOfSettledSessions().map(EngineDO.Session::toClientActiveSession) - .find { session -> session.topic == topic } - } - } - @Throws(IllegalStateException::class) override fun getPendingSessionRequests(topic: String): List { checkEngineInitialization() @@ -471,79 +433,6 @@ class SignProtocol(private val koinApp: KoinApplication = wcKoinApp) : SignInter return runBlocking { signEngine.getListOfVerifyContexts().map { verifyContext -> verifyContext.toCore() } } } - @Deprecated( - message = "Replaced with the same name method but onSuccess callback returns a Pairing URL", - replaceWith = ReplaceWith(expression = "fun connect(connect: Sign.Params.Connect, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - @Throws(IllegalStateException::class) - override fun connect( - connect: Sign.Params.Connect, - onSuccess: () -> Unit, - onError: (Sign.Model.Error) -> Unit, - ) { - checkEngineInitialization() - scope.launch { - try { - signEngine.proposeSession( - connect.namespaces?.toMapOfEngineNamespacesRequired(), - connect.optionalNamespaces?.toMapOfEngineNamespacesOptional(), - connect.properties, - connect.pairing.toPairing(), - onSuccess = { onSuccess() }, - onFailure = { error -> onError(Sign.Model.Error(error)) } - ) - } catch (error: Exception) { - onError(Sign.Model.Error(error)) - } - } - } - - @Deprecated( - "The onSuccess callback has been replaced with a new callback that returns Sign.Model.SentRequest", - replaceWith = ReplaceWith("this.request(request, onSuccessWithSentRequest, onError)", "com.walletconnect.sign.client") - ) - @Throws(IllegalStateException::class) - override fun request( - request: Sign.Params.Request, - onSuccess: (Sign.Params.Request) -> Unit, - onSuccessWithSentRequest: (Sign.Model.SentRequest) -> Unit, - onError: (Sign.Model.Error) -> Unit, - ) { - checkEngineInitialization() - - scope.launch { - try { - signEngine.sessionRequest( - request = request.toEngineDORequest(), - onSuccess = { onSuccess(request) }, - onFailure = { error -> onError(Sign.Model.Error(error)) } - ) - } catch (error: Exception) { - onError(Sign.Model.Error(error)) - } - } - } - - @Deprecated( - "Getting a list of Pairings will be moved to CoreClient to make pairing SDK agnostic", - replaceWith = ReplaceWith("CoreClient.Pairing.getPairings()", "com.walletconnect.android.CoreClient") - ) - @Throws(IllegalStateException::class) - override fun getListOfSettledPairings(): List { - checkEngineInitialization() - return runBlocking { signEngine.getListOfSettledPairings().map(EngineDO.PairingSettle::toClientSettledPairing) } - } - - @Deprecated( - "The return type of getPendingRequests methods has been replaced with SessionRequest list", - replaceWith = ReplaceWith("getPendingSessionRequests(topic: String): List") - ) - @Throws(IllegalStateException::class) - override fun getPendingRequests(topic: String): List { - checkEngineInitialization() - return runBlocking { signEngine.getPendingRequests(Topic(topic)).mapToPendingRequests() } - } - // TODO: Uncomment once reinit scope logic is added // fun shutdown() { // scope.cancel() diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/client/mapper/ClientMapper.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/mapper/ClientMapper.kt new file mode 100644 index 000000000..595ad2ecc --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/mapper/ClientMapper.kt @@ -0,0 +1,463 @@ +@file:JvmSynthetic + +package com.reown.sign.client.mapper + +import com.reown.android.Core +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.ConnectionState +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.RECAPS_STATEMENT +import com.reown.android.internal.common.signing.cacao.getStatement +import com.reown.android.utils.toClient +import com.reown.sign.client.Sign +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.EngineDO + +@JvmSynthetic +internal fun Sign.Model.JsonRpcResponse.toJsonRpcResponse(): JsonRpcResponse = + when (this) { + is Sign.Model.JsonRpcResponse.JsonRpcResult -> this.toRpcResult() + is Sign.Model.JsonRpcResponse.JsonRpcError -> this.toRpcError() + } + +@JvmSynthetic +internal fun EngineDO.SettledSessionResponse.toClientSettledSessionResponse(): Sign.Model.SettledSessionResponse = + when (this) { + is EngineDO.SettledSessionResponse.Result -> Sign.Model.SettledSessionResponse.Result(settledSession.toClientActiveSession()) + is EngineDO.SettledSessionResponse.Error -> Sign.Model.SettledSessionResponse.Error(errorMessage) + } + +@JvmSynthetic +internal fun EngineDO.SessionAuthenticateResponse.toClientSessionAuthenticateResponse(): Sign.Model.SessionAuthenticateResponse = + when (this) { + is EngineDO.SessionAuthenticateResponse.Result -> Sign.Model.SessionAuthenticateResponse.Result(id, cacaos.toClient(), session?.toClientActiveSession()) + is EngineDO.SessionAuthenticateResponse.Error -> Sign.Model.SessionAuthenticateResponse.Error(id, code, message) + } + +@JvmSynthetic +internal fun EngineDO.SessionUpdateNamespacesResponse.toClientUpdateSessionNamespacesResponse(): Sign.Model.SessionUpdateResponse = + when (this) { + is EngineDO.SessionUpdateNamespacesResponse.Result -> Sign.Model.SessionUpdateResponse.Result(topic.value, namespaces.toMapOfClientNamespacesSession()) + is EngineDO.SessionUpdateNamespacesResponse.Error -> Sign.Model.SessionUpdateResponse.Error(errorMessage) + } + +@JvmSynthetic +internal fun EngineDO.JsonRpcResponse.toClientJsonRpcResponse(): Sign.Model.JsonRpcResponse = + when (this) { + is EngineDO.JsonRpcResponse.JsonRpcResult -> this.toClientJsonRpcResult() + is EngineDO.JsonRpcResponse.JsonRpcError -> this.toClientJsonRpcError() + } + +@JvmSynthetic +internal fun EngineDO.SessionProposal.toClientSessionProposal(): Sign.Model.SessionProposal = + Sign.Model.SessionProposal( + pairingTopic, + name, + description, + url, + icons, + redirect, + requiredNamespaces.toMapOfClientNamespacesProposal(), + optionalNamespaces.toMapOfClientNamespacesProposal(), + properties, + proposerPublicKey, + relayProtocol, + relayData + ) + +@JvmSynthetic +internal fun EngineDO.VerifyContext.toCore(): Sign.Model.VerifyContext = + Sign.Model.VerifyContext(id, origin, this.validation.toClientValidation(), verifyUrl, isScam) + +internal fun Validation.toClientValidation(): Sign.Model.Validation = + when (this) { + Validation.VALID -> Sign.Model.Validation.VALID + Validation.INVALID -> Sign.Model.Validation.INVALID + Validation.UNKNOWN -> Sign.Model.Validation.UNKNOWN + } + +@JvmSynthetic +internal fun EngineDO.SessionRequest.toClientSessionRequest(): Sign.Model.SessionRequest = + Sign.Model.SessionRequest( + topic = topic, + chainId = chainId, + peerMetaData = peerAppMetaData?.toClient(), + request = Sign.Model.SessionRequest.JSONRPCRequest( + id = request.id, + method = request.method, + params = request.params + ) + ) + +@JvmSynthetic +internal fun Sign.Model.PayloadParams.toEngine(): EngineDO.PayloadParams = with(this) { + EngineDO.PayloadParams( + type = CacaoType.CAIP222.header, + chains = chains, + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + iat = iat, + version = "1" + ) +} + +@JvmSynthetic +internal fun Sign.Params.Authenticate.toAuthenticate(): EngineDO.Authenticate = with(this) { + EngineDO.Authenticate( + type = CacaoType.EIP4361.header, + chains = chains, + domain = domain, + aud = uri, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + methods = methods, + expiry = expiry + ) +} + +@JvmSynthetic +internal fun Sign.Model.PayloadParams.toCacaoPayload(issuer: String): Sign.Model.Cacao.Payload = with(this) { + Sign.Model.Cacao.Payload(issuer, domain, aud, "1", nonce, iat = iat, nbf, exp, getStatement(), requestId, resources) +} + +private fun Sign.Model.PayloadParams.getStatement() = + if (statement?.contains(RECAPS_STATEMENT) == true) { + statement + } else { + Pair(statement, resources).getStatement() + } + +@JvmSynthetic +internal fun List.toCommon(): List = this.map { + with(it) { + Cacao( + Cacao.Header(header.t), + Cacao.Payload( + payload.iss, + payload.domain, + payload.aud, + payload.version, + payload.nonce, + payload.iat, + payload.nbf, + payload.exp, + payload.statement, + payload.requestId, + payload.resources + ), + Cacao.Signature(signature.t, signature.s, signature.m) + ) + } +} + +@JvmSynthetic +internal fun List.toClient(): List = this.map { + with(it) { + Sign.Model.Cacao( + Sign.Model.Cacao.Header(header.t), + Sign.Model.Cacao.Payload( + payload.iss, + payload.domain, + payload.aud, + payload.version, + payload.nonce, + payload.iat, + payload.nbf, + payload.exp, + payload.statement, + payload.requestId, + payload.resources + ), + Sign.Model.Cacao.Signature(signature.t, signature.s, signature.m) + ) + } +} + +@JvmSynthetic +internal fun Sign.Model.JsonRpcResponse.JsonRpcResult.toRpcResult(): JsonRpcResponse.JsonRpcResult = JsonRpcResponse.JsonRpcResult(id, result = result) + +@JvmSynthetic +internal fun Sign.Model.JsonRpcResponse.JsonRpcError.toRpcError(): JsonRpcResponse.JsonRpcError = JsonRpcResponse.JsonRpcError(id, error = JsonRpcResponse.Error(code, message)) + +@JvmSynthetic +internal fun Sign.Model.SessionEvent.toEngineEvent(chainId: String): EngineDO.Event = EngineDO.Event(name, data, chainId) + +@JvmSynthetic +internal fun EngineDO.SessionDelete.toClientDeletedSession(): Sign.Model.DeletedSession = + Sign.Model.DeletedSession.Success(topic, reason) + +@JvmSynthetic +internal fun EngineDO.SessionEvent.toClientSessionEvent(): Sign.Model.SessionEvent = + Sign.Model.SessionEvent(name, data) + +@JvmSynthetic +internal fun EngineDO.SessionAuthenticateEvent.toClientSessionAuthenticate(): Sign.Model.SessionAuthenticate = with(payloadParams) { + Sign.Model.SessionAuthenticate( + id, + pairingTopic, + participant.toClient(), + payloadParams.toClient(), + expiryTimestamp + ) +} + +@JvmSynthetic +internal fun EngineDO.Participant.toClient(): Sign.Model.SessionAuthenticate.Participant = Sign.Model.SessionAuthenticate.Participant(publicKey, metadata.toClient()) + +@JvmSynthetic +internal fun EngineDO.PayloadParams.toClient(): Sign.Model.PayloadParams = Sign.Model.PayloadParams( + type = type, + chains = chains, + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + iat = iat +) + +@JvmSynthetic +internal fun EngineDO.SessionEvent.toClientEvent(): Sign.Model.Event = + Sign.Model.Event(topic, name, data, chainId) + +@JvmSynthetic +internal fun EngineDO.Session.toClientActiveSession(): Sign.Model.Session = + Sign.Model.Session( + pairingTopic, + topic.value, + expiry.seconds, + requiredNamespaces.toMapOfClientNamespacesProposal(), + optionalNamespaces?.toMapOfClientNamespacesProposal(), + namespaces.toMapOfClientNamespacesSession(), + peerAppMetaData?.toClient() + ) + +@JvmSynthetic +internal fun EngineDO.SessionExtend.toClientActiveSession(): Sign.Model.Session = + Sign.Model.Session( + pairingTopic, + topic.value, + expiry.seconds, + requiredNamespaces.toMapOfClientNamespacesProposal(), + optionalNamespaces?.toMapOfClientNamespacesProposal(), + namespaces.toMapOfClientNamespacesSession(), + peerAppMetaData?.toClient() + ) + +@JvmSynthetic +internal fun EngineDO.SessionRejected.toClientSessionRejected(): Sign.Model.RejectedSession = + Sign.Model.RejectedSession(topic, reason) + +@JvmSynthetic +internal fun EngineDO.SessionApproved.toClientSessionApproved(): Sign.Model.ApprovedSession = + Sign.Model.ApprovedSession(topic, peerAppMetaData?.toClient(), namespaces.toMapOfClientNamespacesSession(), accounts) + +@JvmSynthetic +internal fun Map.toMapOfClientNamespacesSession(): Map = + this.mapValues { (_, namespace) -> + Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Sign.Params.Request.toEngineDORequest(): EngineDO.Request = + EngineDO.Request(sessionTopic, method, params, chainId, expiry?.let { Expiry(it) }) + +@JvmSynthetic +internal fun Sign.Params.Request.toSentRequest(requestId: Long): Sign.Model.SentRequest = + Sign.Model.SentRequest(requestId, sessionTopic, method, params, chainId) + +@JvmSynthetic +internal fun EngineDO.JsonRpcResponse.JsonRpcResult.toClientJsonRpcResult(): Sign.Model.JsonRpcResponse.JsonRpcResult = + Sign.Model.JsonRpcResponse.JsonRpcResult(id, result) + +@JvmSynthetic +internal fun EngineDO.SessionUpdateNamespaces.toClientSessionsNamespaces(): Sign.Model.UpdatedSession = + Sign.Model.UpdatedSession(topic.value, namespaces.toMapOfClientNamespacesSession()) + +@JvmSynthetic +internal fun EngineDO.JsonRpcResponse.JsonRpcError.toClientJsonRpcError(): Sign.Model.JsonRpcResponse.JsonRpcError = + Sign.Model.JsonRpcResponse.JsonRpcError(id, code = error.code, message = error.message) + +@JvmSynthetic +internal fun EngineDO.PairingSettle.toClientSettledPairing(): Sign.Model.Pairing = + Sign.Model.Pairing(topic.value, appMetaData?.toClient()) + +@JvmSynthetic +internal fun List>.mapToPendingRequests(): List = map { request -> + Sign.Model.PendingRequest( + request.id, + request.topic.value, + request.method, + request.chainId, + request.params + ) +} + +@JvmSynthetic +internal fun List.mapToPendingSessionRequests(): List = + map { request -> request.toClientSessionRequest() } + +@JvmSynthetic +internal fun Request.toClient(): Sign.Model.SessionAuthenticate = + Sign.Model.SessionAuthenticate( + id = id, + topic = topic.value, + participant = Sign.Model.SessionAuthenticate.Participant(params.requester.publicKey, params.requester.metadata.toClient()), + payloadParams = params.toClient(), + expiry = params.expiryTimestamp + ) + +@JvmSynthetic +internal fun SignParams.SessionAuthenticateParams.toClient(): Sign.Model.PayloadParams = with(this.authPayload) { + Sign.Model.PayloadParams( + type = type, + chains = chains, + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + iat = iat + ) +} + +@JvmSynthetic +internal fun EngineDO.SessionPayloadResponse.toClientSessionPayloadResponse(): Sign.Model.SessionRequestResponse = + Sign.Model.SessionRequestResponse(topic, chainId, method, result.toClientJsonRpcResponse()) + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesRequired(): Map = + mapValues { (_, namespace) -> + EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesOptional(): Map = + mapValues { (_, namespace) -> + EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfClientNamespacesProposal(): Map = + mapValues { (_, namespace) -> + Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesSession(): Map = + mapValues { (_, namespace) -> + EngineDO.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toProposalNamespacesVO(): Map = + mapValues { (_, namespace) -> + Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) + } + +@JvmSynthetic +internal fun Map.toSessionNamespacesVO(): Map = + mapValues { (_, namespace) -> + Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toCore(): Map = + mapValues { (_, namespace) -> + Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toSign(): Map = + mapValues { (_, namespace) -> + Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun ConnectionState.toClientConnectionState(): Sign.Model.ConnectionState = + Sign.Model.ConnectionState(isAvailable) + +@JvmSynthetic +internal fun EngineDO.ExpiredProposal.toClient(): Sign.Model.ExpiredProposal = + Sign.Model.ExpiredProposal(pairingTopic, proposerPublicKey) + +@JvmSynthetic +internal fun EngineDO.ExpiredRequest.toClient(): Sign.Model.ExpiredRequest = + Sign.Model.ExpiredRequest(topic, id) + +@JvmSynthetic +internal fun SDKError.toClientError(): Sign.Model.Error = + Sign.Model.Error(this.exception) + +@JvmSynthetic +internal fun Core.Model.Message.SessionProposal.toSign(): Sign.Model.Message.SessionProposal = + Sign.Model.Message.SessionProposal( + id, + pairingTopic, + name, + description, + url, + icons, + redirect, + requiredNamespaces.toSign(), + optionalNamespaces.toSign(), + properties, + proposerPublicKey, + relayProtocol, + relayData + ) + +@JvmSynthetic +internal fun Core.Model.Message.SessionAuthenticate.toSign(): Sign.Model.Message.SessionAuthenticate = + Sign.Model.Message.SessionAuthenticate( + id, topic, metadata, payloadParams.toClient(), expiry + ) + +private fun Core.Model.Message.SessionAuthenticate.PayloadParams.toClient(): Sign.Model.PayloadParams { + return with(this) { + Sign.Model.PayloadParams( + chains = chains, + domain = domain, + nonce = nonce, + aud = aud, + type = type, + nbf = nbf, + exp = exp, + iat = iat, + statement = requestId, + resources = resources, + requestId = requestId, + ) + } +} + +@JvmSynthetic +internal fun Core.Model.Message.SessionRequest.toSign(): Sign.Model.Message.SessionRequest = + Sign.Model.Message.SessionRequest( + topic, + chainId, + peerMetaData, + Sign.Model.Message.SessionRequest.JSONRPCRequest(request.id, request.method, request.params) + ) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/CacaoSigner.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/CacaoSigner.kt new file mode 100644 index 000000000..4c64f1946 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/CacaoSigner.kt @@ -0,0 +1,16 @@ +package com.reown.sign.client.utils + +import com.reown.android.cacao.signature.ISignatureType +import com.reown.android.utils.cacao.CacaoSignerInterface +import com.reown.sign.client.Sign + +/** + * @deprecated Only added to have backwards compatibility. Newer SDKs should only add CacaoSigner object below. + */ + +@Deprecated("Moved to android-core module, as other SDKs also need CACAO.", ReplaceWith("com.reown.android.internal.common.cacao.signature.SignatureType")) +enum class SignatureType(override val header: String) : ISignatureType { + EIP191("eip191"), EIP1271("eip1271"); +} + +object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/Utils.kt b/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/Utils.kt new file mode 100644 index 000000000..321ae4b14 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/client/utils/Utils.kt @@ -0,0 +1,153 @@ +@file:JvmName("ApprovedNamespacesUtils") + +package com.reown.sign.client.utils + +import android.util.Base64 +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.RECAPS_STATEMENT +import com.reown.android.internal.common.signing.cacao.decodeReCaps +import com.reown.android.internal.common.signing.cacao.getStatement +import com.reown.android.internal.common.signing.cacao.parseReCaps +import com.reown.android.internal.utils.CoreValidator +import com.reown.sign.client.Sign +import com.reown.sign.client.mapper.toCacaoPayload +import com.reown.sign.client.mapper.toCore +import com.reown.sign.client.mapper.toProposalNamespacesVO +import com.reown.sign.client.mapper.toSessionNamespacesVO +import com.reown.sign.common.validator.SignValidator +import org.json.JSONArray +import org.json.JSONObject + +fun generateApprovedNamespaces( + proposal: Sign.Model.SessionProposal, + supportedNamespaces: Map, +): Map { + val supportedNamespacesVO = supportedNamespaces.toSessionNamespacesVO() + val normalizedRequiredNamespaces = normalizeNamespaces(proposal.requiredNamespaces.toProposalNamespacesVO()) + val normalizedOptionalNamespaces = normalizeNamespaces(proposal.optionalNamespaces.toProposalNamespacesVO()) + + SignValidator.validateProposalNamespaces(normalizedRequiredNamespaces) { error -> throw Exception(error.message) } + SignValidator.validateProposalNamespaces(normalizedOptionalNamespaces) { error -> throw Exception(error.message) } + SignValidator.validateSupportedNamespace(supportedNamespacesVO, normalizedRequiredNamespaces) { error -> throw Exception(error.message) } + + if (proposal.requiredNamespaces.isEmpty() && proposal.optionalNamespaces.isEmpty()) { + return supportedNamespacesVO.toCore() + } + + val approvedNamespaces = mutableMapOf() + normalizedRequiredNamespaces.forEach { (key, requiredNamespace) -> + val chains = supportedNamespacesVO[key]?.chains?.filter { chain -> requiredNamespace.chains!!.contains(chain) } ?: emptyList() + val methods = supportedNamespaces[key]?.methods?.filter { method -> requiredNamespace.methods.contains(method) } ?: emptyList() + val events = supportedNamespaces[key]?.events?.filter { event -> requiredNamespace.events.contains(event) } ?: emptyList() + val accounts = chains.flatMap { chain -> supportedNamespaces[key]?.accounts?.filter { account -> SignValidator.getChainFromAccount(account) == chain } ?: emptyList() } + + approvedNamespaces[key] = Namespace.Session(chains = chains, methods = methods, events = events, accounts = accounts) + } + + normalizedOptionalNamespaces.forEach { (key, optionalNamespace) -> + if (!supportedNamespaces.containsKey(key)) return@forEach + val chains = supportedNamespacesVO[key]?.chains?.filter { chain -> optionalNamespace.chains!!.contains(chain) } ?: emptyList() + val methods = supportedNamespaces[key]?.methods?.filter { method -> optionalNamespace.methods.contains(method) } ?: emptyList() + val events = supportedNamespaces[key]?.events?.filter { event -> optionalNamespace.events.contains(event) } ?: emptyList() + val accounts = chains.flatMap { chain -> supportedNamespaces[key]?.accounts?.filter { account -> SignValidator.getChainFromAccount(account) == chain } ?: emptyList() } + + approvedNamespaces[key] = Namespace.Session( + chains = approvedNamespaces[key]?.chains?.plus(chains)?.distinct() ?: chains, + methods = approvedNamespaces[key]?.methods?.plus(methods)?.distinct() ?: methods, + events = approvedNamespaces[key]?.events?.plus(events)?.distinct() ?: events, + accounts = approvedNamespaces[key]?.accounts?.plus(accounts)?.distinct() ?: accounts + ) + } + + return approvedNamespaces.toCore() +} + +internal fun normalizeNamespaces(namespaces: Map): Map { + if (SignValidator.isNamespaceKeyRegexCompliant(namespaces)) return namespaces + return mutableMapOf().apply { + namespaces.forEach { (key, namespace) -> + val normalizedKey = normalizeKey(key) + this[normalizedKey] = Namespace.Proposal( + chains = getChains(normalizedKey).plus(getNamespaceChains(key, namespace)), + methods = getMethods(normalizedKey).plus(namespace.methods), + events = getEvents(normalizedKey).plus(namespace.events) + ) + } + }.toMap() +} + +private fun getNamespaceChains(key: String, namespace: Namespace) = if (CoreValidator.isChainIdCAIP2Compliant(key)) listOf(key) else namespace.chains!! +private fun normalizeKey(key: String): String = if (CoreValidator.isChainIdCAIP2Compliant(key)) SignValidator.getNamespaceKeyFromChainId(key) else key +private fun MutableMap.getChains(normalizedKey: String) = (this[normalizedKey]?.chains ?: emptyList()) +private fun MutableMap.getMethods(normalizedKey: String) = (this[normalizedKey]?.methods ?: emptyList()) +private fun MutableMap.getEvents(normalizedKey: String) = (this[normalizedKey]?.events ?: emptyList()) + +fun generateAuthObject(payload: Sign.Model.PayloadParams, issuer: String, signature: Sign.Model.Cacao.Signature): Sign.Model.Cacao { + return Sign.Model.Cacao( + header = Sign.Model.Cacao.Header(t = CacaoType.CAIP222.header), + payload = payload.toCacaoPayload(issuer), + signature = signature + ) +} + +fun generateAuthPayloadParams(payloadParams: Sign.Model.PayloadParams, supportedChains: List, supportedMethods: List): Sign.Model.PayloadParams { + val reCapsJson: String? = payloadParams.resources.decodeReCaps() + if (reCapsJson.isNullOrEmpty() || !reCapsJson.contains("eip155")) return payloadParams + + val sessionReCaps = reCapsJson.parseReCaps()["eip155"] + val requestedMethods = sessionReCaps!!.keys.map { key -> key.substringAfter('/') } + val requestedChains = payloadParams.chains + + val sessionChains = requestedChains.intersect(supportedChains.toSet()).toList().distinct() + val sessionMethods = requestedMethods.intersect(supportedMethods.toSet()).toList().distinct() + + if (sessionChains.isEmpty()) throw Exception("Unsupported chains") + if (sessionMethods.isEmpty()) throw Exception("Unsupported methods") + + if (!sessionChains.all { chain -> CoreValidator.isChainIdCAIP2Compliant(chain) }) throw Exception("Chains are not CAIP-2 compliant") + if (!sessionChains.all { chain -> SignValidator.getNamespaceKeyFromChainId(chain) == "eip155" }) throw Exception("Only eip155(EVM) is supported") + + val actionsJsonObject = JSONObject() + val chainsJsonArray = JSONArray() + sessionChains.forEach { chain -> chainsJsonArray.put(chain) } + sessionMethods.forEach { method -> actionsJsonObject.put("request/$method", JSONArray().put(0, JSONObject().put("chains", chainsJsonArray))) } + + val recaps = JSONObject(reCapsJson) + val att = recaps.getJSONObject("att") + att.put("eip155", actionsJsonObject) + val stringReCaps = recaps.toString().replace("\\/", "/") + val base64Recaps = Base64.encodeToString(stringReCaps.toByteArray(Charsets.UTF_8), Base64.NO_WRAP or Base64.NO_PADDING) + val newReCapsUrl = "${RECAPS_PREFIX}$base64Recaps" + + if (payloadParams.resources == null) { + payloadParams.resources = listOf(newReCapsUrl) + } else { + payloadParams.resources = payloadParams.resources!!.dropLast(1).plus(newReCapsUrl) + } + + return with(payloadParams) { + Sign.Model.PayloadParams( + chains = sessionChains, + domain = domain, + nonce = nonce, + aud = aud, + type = type, + nbf = nbf, + exp = exp, + iat = iat, + statement = getStatement(), + resources = resources, + requestId = requestId + ) + } +} + +private fun Sign.Model.PayloadParams.getStatement() = + if (statement?.contains(RECAPS_STATEMENT) == true) { + statement + } else { + Pair(statement, resources).getStatement() + } + diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionEventVOJsonAdapter.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionEventVOJsonAdapter.kt similarity index 97% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionEventVOJsonAdapter.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionEventVOJsonAdapter.kt index 92b28d099..f09b3612a 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionEventVOJsonAdapter.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionEventVOJsonAdapter.kt @@ -1,11 +1,11 @@ -package com.walletconnect.sign.common.adapters +package com.reown.sign.common.adapters import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter import com.squareup.moshi.Moshi import com.squareup.moshi.internal.Util -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionEventVO +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionEventVO import org.json.JSONArray import org.json.JSONObject diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionRequestVOJsonAdapter.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionRequestVOJsonAdapter.kt similarity index 97% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionRequestVOJsonAdapter.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionRequestVOJsonAdapter.kt index 25bee84bf..f60467279 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/adapters/SessionRequestVOJsonAdapter.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/adapters/SessionRequestVOJsonAdapter.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.adapters +package com.reown.sign.common.adapters import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter import com.squareup.moshi.Moshi import com.squareup.moshi.internal.Util -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO import org.json.JSONArray import org.json.JSONObject import kotlin.String diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/Messages.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/Messages.kt new file mode 100644 index 000000000..25c864ef1 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/Messages.kt @@ -0,0 +1,36 @@ +package com.reown.sign.common.exceptions + +internal const val NO_SEQUENCE_FOR_TOPIC_MESSAGE: String = "Cannot find sequence for given topic: " +internal const val UNAUTHORIZED_UPDATE_MESSAGE: String = + "The update() was called by the unauthorized peer. Must be called by controller client." +internal const val UNAUTHORIZED_EXTEND_MESSAGE: String = + "The extend() was called by the unauthorized peer. Must be called by controller client." +internal const val UNAUTHORIZED_EMIT_MESSAGE: String = + "The emit() was called by the unauthorized peer. Must be called by controller client." +internal const val SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE: String = "Session is not acknowledged, topic: " + +internal const val EMPTY_NAMESPACES_MESSAGE: String = "Session namespaces MUST not be empty" +internal const val NAMESPACE_CHAINS_MISSING_MESSAGE: String = "Chains must not be empty" +internal const val NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE: String = "Chains must not be null" +internal const val NAMESPACE_CHAINS_CAIP_2_MESSAGE: String = "Chains must be CAIP-2 compliant" +internal const val NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE: String = "Chains must be defined in matching namespace" +internal const val NAMESPACE_KEYS_INVALID_FORMAT: String = "Invalid namespace id format" + +internal const val NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE: String = "Accounts must be CAIP-10 compliant" +internal const val NAMESPACE_METHODS_MISSING_MESSAGE: String = "All required namespaces must be approved: not all methods are approved" +internal const val NAMESPACE_EVENTS_MISSING_MESSAGE: String = "All events must be approved: not all events are approved" +internal const val NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE: String = "Accounts must be defined in matching namespace" +internal const val NAMESPACE_KEYS_MISSING_MESSAGE: String = "All required namespaces must be approved" + +internal const val UNAUTHORIZED_METHOD_MESSAGE: String = "Unauthorized method is not authorized for given chain" +internal const val UNAUTHORIZED_EVENT_MESSAGE: String = "Unauthorized event is not authorized for given chain" +internal const val INVALID_EVENT_MESSAGE: String = "Event name and data fields cannot be empty. ChainId must be CAIP-2 compliant" +internal const val INVALID_REQUEST_MESSAGE: String = "Request topic, method and params fields cannot be empty. ChainId must be CAIP-2 compliant" +internal const val INVALID_EXTEND_TIME: String = "Extend time is out of range" +internal const val INVALID_SESSION_PROPERTIES: String = "Invalid Session Properties requested" + +internal const val CLIENT_ALREADY_INITIALIZED: String = "SignClient already initialized" +internal const val INVALID_SIGN_PARAMS_TYPE: String = "Invalid Sign params type" + +internal const val MISSING_SESSION_AUTHENTICATE_REQUEST: String = "Missing session authenticate request, expired" +internal const val INVALID_CACAO_EXCEPTION: String = "Invalid Cacao exception" \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/PeerError.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/PeerError.kt new file mode 100644 index 000000000..7685d4510 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/PeerError.kt @@ -0,0 +1,128 @@ +package com.reown.sign.common.exceptions + +import com.reown.android.internal.common.model.type.Error + +/** + * Documentation: https://github.com/WalletConnect/walletconnect-specs/blob/main/sign/error-codes.md + */ +sealed class PeerError : Error { + sealed class Invalid : PeerError() { + + data class Method(val reason: String) : Invalid() { + override val message: String = "Invalid session request: $reason" + override val code: Int = 1001 + } + + data class Event(val reason: String) : Invalid() { + override val message: String = "Invalid event request: $reason" + override val code: Int = 1002 + } + + data class UpdateRequest(val reason: String) : Invalid() { + override val message: String = "Invalid update namespace request: $reason" + override val code: Int = 1003 + } + + data class ExtendRequest(val reason: String) : Invalid() { + override val message = "Invalid session extend request: $reason" + override val code: Int = 1004 + } + + data class SessionSettleRequest(override val message: String) : Invalid() { + override val code: Int = 1005 + } + } + + sealed class Unauthorized : PeerError() { + + data class Method(val reason: String) : Unauthorized() { + override val message: String = "Unauthorized session request: $reason" + override val code: Int = 3001 + } + + data class Event(val reason: String) : Unauthorized() { + override val message: String = "Unauthorized event request: $reason" + override val code: Int = 3002 + } + + data class UpdateRequest(val sequence: String) : Unauthorized() { + override val message: String = "Unauthorized update $sequence namespace request" + override val code: Int = 3003 + } + + data class ExtendRequest(val sequence: String) : Unauthorized() { + override val message: String = "Unauthorized $sequence extend request" + override val code: Int = 3004 + } + + data class Chain(override val message: String) : Unauthorized() { + override val code: Int = 3005 + } + } + + sealed class EIP1193 : PeerError() { + + data class UserRejectedRequest(override val message: String) : EIP1193() { + override val code: Int = 4001 + } + } + + sealed class CAIP25 : PeerError() { + + data class UserRejected(override val message: String) : CAIP25() { + override val code: Int = 5000 + } + + data class UserRejectedChains(override val message: String) : CAIP25() { + override val code: Int = 5001 + } + + data class UserRejectedMethods(override val message: String) : CAIP25() { + override val code: Int = 5002 + } + + data class UserRejectedEvents(override val message: String) : CAIP25() { + override val code: Int = 5003 + } + + data class UnsupportedChains(override val message: String) : CAIP25() { + override val code: Int = 5100 + } + + data class UnsupportedMethods(override val message: String) : CAIP25() { + override val code: Int = 5101 + } + + data class UnsupportedEvents(override val message: String) : CAIP25() { + override val code: Int = 5102 + } + + data class UnsupportedAccounts(override val message: String) : CAIP25() { + override val code: Int = 5103 + } + + data class UnsupportedNamespaceKey(override val message: String) : CAIP25() { + override val code: Int = 5104 + } + + data class InvalidSessionPropertiesObject(override val message: String) : CAIP25() { + override val code: Int = 5200 + } + + data class EmptySessionNamespaces(override val message: String) : CAIP25() { + override val code: Int = 5201 + } + } + + sealed class Failure : PeerError() { + + data class SessionSettlementFailed(val reason: String) : Failure() { + override val message: String = "Invalid Session Settle Request: $reason" + override val code: Int = 7000 + } + + data class NoSessionForTopic(override val message: String) : Failure() { + override val code: Int = 7001 + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/SignExceptions.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/SignExceptions.kt similarity index 90% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/SignExceptions.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/SignExceptions.kt index 2da17dd00..2dbadf058 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/SignExceptions.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/exceptions/SignExceptions.kt @@ -1,6 +1,6 @@ -package com.walletconnect.sign.common.exceptions +package com.reown.sign.common.exceptions -import com.walletconnect.android.internal.common.exception.WalletConnectException +import com.reown.android.internal.common.exception.WalletConnectException class UnauthorizedPeerException(override val message: String?) : WalletConnectException(message) class UnauthorizedMethodException(override val message: String?) : WalletConnectException(message) diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/Request.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/Request.kt new file mode 100644 index 000000000..cc04522ce --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/Request.kt @@ -0,0 +1,15 @@ +package com.reown.sign.common.model + +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.TransportType +import com.reown.foundation.common.model.Topic + +internal data class Request( + val id: Long, + val topic: Topic, + val method: String, + val chainId: String?, + val params: T, + val expiry: Expiry? = null, + val transportType: TransportType? +) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/type/Sequences.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/type/Sequences.kt new file mode 100644 index 000000000..a4c696225 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/type/Sequences.kt @@ -0,0 +1,5 @@ +package com.reown.sign.common.model.type + +enum class Sequences { + SESSION, PAIRING +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/PayloadParams.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/PayloadParams.kt new file mode 100644 index 000000000..24d4089a0 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/PayloadParams.kt @@ -0,0 +1,32 @@ +package com.reown.sign.common.model.vo.clientsync.common + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class PayloadParams( + @Json(name = "type") + val type: String, + @Json(name = "chains") + val chains: List, + @Json(name = "domain") + val domain: String, + @Json(name = "aud") + val aud: String, + @Json(name = "nonce") + val nonce: String, + @Json(name = "version") + val version: String, + @Json(name = "iat") + val iat: String, + @Json(name = "nbf") + val nbf: String?, + @Json(name = "exp") + val exp: String?, + @Json(name = "statement") + val statement: String?, + @Json(name = "requestId") + val requestId: String?, + @Json(name = "resources") + val resources: List?, +) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/Requester.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/Requester.kt new file mode 100644 index 000000000..2ef5746ed --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/Requester.kt @@ -0,0 +1,13 @@ +package com.reown.sign.common.model.vo.clientsync.common + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.AppMetaData + +@JsonClass(generateAdapter = true) +data class Requester( + @Json(name = "publicKey") + val publicKey: String, + @Json(name = "metadata") + val metadata: AppMetaData +) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/SessionParticipant.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/SessionParticipant.kt new file mode 100644 index 000000000..db0eebf5a --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/common/SessionParticipant.kt @@ -0,0 +1,15 @@ +@file:JvmSynthetic + +package com.reown.sign.common.model.vo.clientsync.common + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.AppMetaData + +@JsonClass(generateAdapter = true) +internal data class SessionParticipant( + @Json(name = "publicKey") + val publicKey: String, + @Json(name = "metadata") + val metadata: AppMetaData, +) diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/SignRpc.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/SignRpc.kt similarity index 92% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/SignRpc.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/SignRpc.kt index d273d0f14..adb6cad09 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/SignRpc.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/SignRpc.kt @@ -1,13 +1,13 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.model.vo.clientsync.session +package com.reown.sign.common.model.vo.clientsync.session import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.type.JsonRpcClientSync -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.util.generateId +import com.reown.android.internal.common.model.type.JsonRpcClientSync +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.util.generateId internal sealed class SignRpc : JsonRpcClientSync { diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/params/SignParams.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/params/SignParams.kt similarity index 78% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/params/SignParams.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/params/SignParams.kt index 90060b6ac..8e5815db7 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/params/SignParams.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/params/SignParams.kt @@ -1,19 +1,19 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.model.vo.clientsync.session.params +package com.reown.sign.common.model.vo.clientsync.session.params import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SessionProposer -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.sign.common.model.vo.clientsync.common.PayloadParams -import com.walletconnect.sign.common.model.vo.clientsync.common.Requester -import com.walletconnect.sign.common.model.vo.clientsync.common.SessionParticipant -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionEventVO -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO -import com.walletconnect.utils.DefaultId +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SessionProposer +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.sign.common.model.vo.clientsync.common.PayloadParams +import com.reown.sign.common.model.vo.clientsync.common.Requester +import com.reown.sign.common.model.vo.clientsync.common.SessionParticipant +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionEventVO +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.utils.DefaultId internal sealed class SignParams : CoreSignParams() { diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt similarity index 77% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt index d1c3d204c..96ed537bd 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionEventVO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.model.vo.clientsync.session.payload +package com.reown.sign.common.model.vo.clientsync.session.payload import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt similarity index 82% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt index 9dbd748fb..b7b0bf183 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/clientsync/session/payload/SessionRequestVO.kt @@ -1,6 +1,6 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.model.vo.clientsync.session.payload +package com.reown.sign.common.model.vo.clientsync.session.payload import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/proposal/ProposalVO.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/proposal/ProposalVO.kt new file mode 100644 index 000000000..4576d30eb --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/proposal/ProposalVO.kt @@ -0,0 +1,27 @@ +package com.reown.sign.common.model.vo.proposal + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.Redirect +import com.reown.foundation.common.model.Topic + +internal data class ProposalVO( + val requestId: Long, + val pairingTopic: Topic, + val name: String, + val description: String, + val url: String, + val icons: List, + val redirect: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map, + val properties: Map?, + val proposerPublicKey: String, + val relayProtocol: String, + val relayData: String?, + val expiry: Expiry? +) { + val appMetaData: AppMetaData + get() = AppMetaData(name = name, description = description, url = url, icons = icons, redirect = Redirect(native = redirect)) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/sequence/SessionVO.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/sequence/SessionVO.kt similarity index 84% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/sequence/SessionVO.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/sequence/SessionVO.kt index 11ef42f48..38c35129e 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/sequence/SessionVO.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/model/vo/sequence/SessionVO.kt @@ -1,20 +1,20 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.model.vo.sequence +package com.reown.sign.common.model.vo.sequence -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.Sequence -import com.walletconnect.android.internal.utils.ACTIVE_SESSION -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.vo.clientsync.common.SessionParticipant -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toMapOfNamespacesVOSession +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.Sequence +import com.reown.android.internal.utils.ACTIVE_SESSION +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.vo.clientsync.common.SessionParticipant +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toMapOfNamespacesVOSession internal data class SessionVO( override val topic: Topic, diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/validator/SignValidator.kt b/protocol/sign/src/main/kotlin/com/reown/sign/common/validator/SignValidator.kt similarity index 92% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/common/validator/SignValidator.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/common/validator/SignValidator.kt index bd6f6e88a..ef9695ca7 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/validator/SignValidator.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/common/validator/SignValidator.kt @@ -1,24 +1,24 @@ @file:JvmSynthetic -package com.walletconnect.sign.common.validator +package com.reown.sign.common.validator import android.annotation.SuppressLint -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.utils.CoreValidator.isAccountIdCAIP10Compliant -import com.walletconnect.android.internal.utils.CoreValidator.isChainIdCAIP2Compliant -import com.walletconnect.android.internal.utils.CoreValidator.isNamespaceRegexCompliant -import com.walletconnect.android.internal.utils.weekInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.exceptions.NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_CAIP_2_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.ValidationError +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.utils.CoreValidator.isAccountIdCAIP10Compliant +import com.reown.android.internal.utils.CoreValidator.isChainIdCAIP2Compliant +import com.reown.android.internal.utils.CoreValidator.isNamespaceRegexCompliant +import com.reown.android.internal.utils.weekInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.exceptions.NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_CAIP_2_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.ValidationError import java.net.URI import java.net.URISyntaxException diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/CallsModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/CallsModule.kt new file mode 100644 index 000000000..2169dffd8 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/CallsModule.kt @@ -0,0 +1,211 @@ +package com.reown.sign.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.Tags +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCase +import com.reown.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.ApproveSessionUseCase +import com.reown.sign.engine.use_case.calls.ApproveSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.DecryptSignMessageUseCase +import com.reown.sign.engine.use_case.calls.DisconnectSessionUseCase +import com.reown.sign.engine.use_case.calls.DisconnectSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.EmitEventUseCase +import com.reown.sign.engine.use_case.calls.EmitEventUseCaseInterface +import com.reown.sign.engine.use_case.calls.ExtendSessionUseCase +import com.reown.sign.engine.use_case.calls.ExtendSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.FormatAuthenticateMessageUseCase +import com.reown.sign.engine.use_case.calls.FormatAuthenticateMessageUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetListOfVerifyContextsUseCase +import com.reown.sign.engine.use_case.calls.GetListOfVerifyContextsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetNamespacesFromReCaps +import com.reown.sign.engine.use_case.calls.GetPairingForSessionAuthenticateUseCase +import com.reown.sign.engine.use_case.calls.GetPairingsUseCase +import com.reown.sign.engine.use_case.calls.GetPairingsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetSessionProposalsUseCase +import com.reown.sign.engine.use_case.calls.GetSessionProposalsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetSessionsUseCase +import com.reown.sign.engine.use_case.calls.GetSessionsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetVerifyContextByIdUseCase +import com.reown.sign.engine.use_case.calls.GetVerifyContextByIdUseCaseInterface +import com.reown.sign.engine.use_case.calls.PairUseCase +import com.reown.sign.engine.use_case.calls.PairUseCaseInterface +import com.reown.sign.engine.use_case.calls.PingUseCase +import com.reown.sign.engine.use_case.calls.PingUseCaseInterface +import com.reown.sign.engine.use_case.calls.ProposeSessionUseCase +import com.reown.sign.engine.use_case.calls.ProposeSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.RejectSessionAuthenticateUseCase +import com.reown.sign.engine.use_case.calls.RejectSessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.RejectSessionUseCase +import com.reown.sign.engine.use_case.calls.RejectSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.RespondSessionRequestUseCase +import com.reown.sign.engine.use_case.calls.RespondSessionRequestUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionAuthenticateUseCase +import com.reown.sign.engine.use_case.calls.SessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionRequestUseCase +import com.reown.sign.engine.use_case.calls.SessionRequestUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionUpdateUseCase +import com.reown.sign.engine.use_case.calls.SessionUpdateUseCaseInterface +import com.reown.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopic +import com.reown.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopicInterface +import com.reown.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCase +import com.reown.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCaseInterface +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@JvmSynthetic +internal fun callsModule() = module { + + single { + ProposeSessionUseCase( + jsonRpcInteractor = get(), + crypto = get(), + selfAppMetaData = get(), + proposalStorageRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + SessionAuthenticateUseCase( + jsonRpcInteractor = get(), + crypto = get(), + selfAppMetaData = get(), + authenticateResponseTopicRepository = get(), + proposeSessionUseCase = get(), + getPairingForSessionAuthenticate = get(), + getNamespacesFromReCaps = get(), + linkModeJsonRpcInteractor = get(), + linkModeStorageRepository = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { PairUseCase(pairingInterface = get()) } + + single { + ApproveSessionUseCase( + proposalStorageRepository = get(), + selfAppMetaData = get(), + crypto = get(), + jsonRpcInteractor = get(), + metadataStorageRepository = get(), + sessionStorageRepository = get(), + verifyContextStorageRepository = get(), + insertEventUseCase = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + ApproveSessionAuthenticateUseCase( + jsonRpcInteractor = get(), + crypto = get(), + cacaoVerifier = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + verifyContextStorageRepository = get(), + getPendingSessionAuthenticateRequest = get(), + selfAppMetaData = get(), + sessionStorageRepository = get(), + metadataStorageRepository = get(), + insertTelemetryEventUseCase = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + linkModeJsonRpcInteractor = get() + ) + } + + single { + RejectSessionAuthenticateUseCase( + jsonRpcInteractor = get(), + crypto = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + verifyContextStorageRepository = get(), + getPendingSessionAuthenticateRequest = get(), + linkModeJsonRpcInteractor = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + insertEventUseCase = get() + ) + } + + single { + RejectSessionUseCase( + verifyContextStorageRepository = get(), + proposalStorageRepository = get(), + jsonRpcInteractor = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + pairingController = get() + ) + } + + single { SessionUpdateUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { + SessionRequestUseCase( + jsonRpcInteractor = get(), + sessionStorageRepository = get(), + linkModeJsonRpcInteractor = get(), + metadataStorageRepository = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + RespondSessionRequestUseCase( + jsonRpcInteractor = get(), + verifyContextStorageRepository = get(), + sessionStorageRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + getPendingJsonRpcHistoryEntryByIdUseCase = get(), + linkModeJsonRpcInteractor = get(), + metadataStorageRepository = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + ) + } + + single(named(AndroidCommonDITags.DECRYPT_SIGN_MESSAGE)) { + val useCase = DecryptSignMessageUseCase( + codec = get(), + serializer = get(), + metadataRepository = get(), + pushMessageStorage = get(), + ) + + get>(named(AndroidCommonDITags.DECRYPT_USE_CASES))[Tags.SESSION_PROPOSE.id.toString()] = useCase + useCase + } + + single { PingUseCase(sessionStorageRepository = get(), jsonRpcInteractor = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { EmitEventUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { ExtendSessionUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { DisconnectSessionUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { GetSessionsUseCase(sessionStorageRepository = get(), metadataStorageRepository = get(), selfAppMetaData = get()) } + + single { GetPairingsUseCase(pairingInterface = get()) } + + single { GetPairingForSessionAuthenticateUseCase(pairingProtocol = get()) } + + single { GetNamespacesFromReCaps() } + + single { GetPendingRequestsUseCaseByTopic(serializer = get(), jsonRpcHistory = get()) } + + single { GetPendingSessionRequestByTopicUseCase(jsonRpcHistory = get(), serializer = get(), metadataStorageRepository = get()) } + + single { GetSessionProposalsUseCase(proposalStorageRepository = get()) } + + single { GetVerifyContextByIdUseCase(verifyContextStorageRepository = get()) } + + single { GetListOfVerifyContextsUseCase(verifyContextStorageRepository = get()) } + + single { FormatAuthenticateMessageUseCase() } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/EngineModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/EngineModule.kt new file mode 100644 index 000000000..04d500d87 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/EngineModule.kt @@ -0,0 +1,95 @@ +@file:JvmSynthetic + +package com.reown.sign.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.sign.engine.domain.SignEngine +import com.reown.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCase +import com.reown.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCaseInterface +import com.reown.sign.json_rpc.domain.DeleteRequestByIdUseCase +import com.reown.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase +import com.reown.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest +import com.reown.sign.json_rpc.domain.GetPendingSessionRequests +import com.reown.sign.json_rpc.domain.GetSessionAuthenticateRequest +import com.reown.sign.json_rpc.domain.GetSessionRequestByIdUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@JvmSynthetic +internal fun engineModule() = module { + + includes(callsModule(), requestsModule(), responsesModule()) + + single { GetPendingSessionRequests(jsonRpcHistory = get(), serializer = get()) } + + single { GetPendingAuthenticateRequestUseCase(jsonRpcHistory = get(), serializer = get()) } + + single { DeleteRequestByIdUseCase(jsonRpcHistory = get(), verifyContextStorageRepository = get()) } + + single { GetPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcHistory = get(), serializer = get()) } + + single { GetSessionRequestByIdUseCase(jsonRpcHistory = get(), serializer = get()) } + + single { GetPendingSessionAuthenticateRequest(jsonRpcHistory = get(), serializer = get()) } + + single { GetSessionAuthenticateRequest(jsonRpcHistory = get(), serializer = get()) } + + single { CacaoVerifier(projectId = get()) } + + single { + SignEngine( + verifyContextStorageRepository = get(), + jsonRpcInteractor = get(), + crypto = get(), + authenticateResponseTopicRepository = get(), + proposalStorageRepository = get(), + authenticateSessionUseCase = get(), + sessionStorageRepository = get(), + metadataStorageRepository = get(), + approveSessionUseCase = get(), + disconnectSessionUseCase = get(), + emitEventUseCase = get(), + extendSessionUseCase = get(), + decryptMessageUseCase = get(named(AndroidCommonDITags.DECRYPT_SIGN_MESSAGE)), + getListOfVerifyContextsUseCase = get(), + getPairingsUseCase = get(), + getPendingRequestsByTopicUseCase = get(), + getPendingSessionRequests = get(), + getSessionProposalsUseCase = get(), + getSessionsUseCase = get(), + onPingUseCase = get(), + getVerifyContextByIdUseCase = get(), + onSessionDeleteUseCase = get(), + onSessionEventUseCase = get(), + onSessionExtendUseCase = get(), + getPendingSessionRequestByTopicUseCase = get(), + onSessionProposalResponseUseCase = get(), + onSessionProposeUse = get(), + onSessionRequestResponseUseCase = get(), + onSessionRequestUseCase = get(), + onSessionSettleResponseUseCase = get(), + onSessionSettleUseCase = get(), + onSessionUpdateResponseUseCase = get(), + onSessionUpdateUseCase = get(), + pairingController = get(), + pairUseCase = get(), + pingUseCase = get(), + proposeSessionUseCase = get(), + rejectSessionUseCase = get(), + respondSessionRequestUseCase = get(), + sessionRequestUseCase = get(), + sessionUpdateUseCase = get(), + onAuthenticateSessionUseCase = get(), + onSessionAuthenticateResponseUseCase = get(), + approveSessionAuthenticateUseCase = get(), + rejectSessionAuthenticateUseCase = get(), + formatAuthenticateMessageUseCase = get(), + deleteRequestByIdUseCase = get(), + getPendingAuthenticateRequestUseCase = get(), + insertEventUseCase = get(), + linkModeJsonRpcInteractor = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/RequestsModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/RequestsModule.kt new file mode 100644 index 000000000..b00ffbab6 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/RequestsModule.kt @@ -0,0 +1,77 @@ +package com.reown.sign.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.sign.engine.use_case.requests.OnPingUseCase +import com.reown.sign.engine.use_case.requests.OnSessionAuthenticateUseCase +import com.reown.sign.engine.use_case.requests.OnSessionDeleteUseCase +import com.reown.sign.engine.use_case.requests.OnSessionEventUseCase +import com.reown.sign.engine.use_case.requests.OnSessionExtendUseCase +import com.reown.sign.engine.use_case.requests.OnSessionProposalUseCase +import com.reown.sign.engine.use_case.requests.OnSessionRequestUseCase +import com.reown.sign.engine.use_case.requests.OnSessionSettleUseCase +import com.reown.sign.engine.use_case.requests.OnSessionUpdateUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@JvmSynthetic +internal fun requestsModule() = module { + + single { + OnSessionProposalUseCase( + pairingController = get(), + jsonRpcInteractor = get(), + proposalStorageRepository = get(), + resolveAttestationIdUseCase = get(), + insertEventUseCase = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + OnSessionAuthenticateUseCase( + jsonRpcInteractor = get(), + resolveAttestationIdUseCase = get(), + logger = get(), + pairingController = get(), + insertTelemetryEventUseCase = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + ) + } + + single { + OnSessionSettleUseCase( + proposalStorageRepository = get(), + jsonRpcInteractor = get(), + pairingController = get(), + metadataStorageRepository = get(), + sessionStorageRepository = get(), + crypto = get(), + selfAppMetaData = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + OnSessionRequestUseCase( + metadataStorageRepository = get(), + sessionStorageRepository = get(), + jsonRpcInteractor = get(), + resolveAttestationIdUseCase = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + ) + } + + single { OnSessionDeleteUseCase(jsonRpcInteractor = get(), crypto = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { OnSessionEventUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { OnSessionUpdateUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { OnSessionExtendUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { OnPingUseCase(jsonRpcInteractor = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/ResponsesModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/ResponsesModule.kt new file mode 100644 index 000000000..f43e6f3ad --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/ResponsesModule.kt @@ -0,0 +1,64 @@ +package com.reown.sign.di + +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.sign.engine.use_case.responses.OnSessionAuthenticateResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionProposalResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionRequestResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionSettleResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionUpdateResponseUseCase +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@JvmSynthetic +internal fun responsesModule() = module { + + single { + OnSessionProposalResponseUseCase( + jsonRpcInteractor = get(), + crypto = get(), + pairingController = get(), + proposalStorageRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + OnSessionSettleResponseUseCase( + crypto = get(), + jsonRpcInteractor = get(), + sessionStorageRepository = get(), + metadataStorageRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)) + ) + } + + single { + OnSessionAuthenticateResponseUseCase( + pairingController = get(), + pairingInterface = get(), + cacaoVerifier = get(), + sessionStorageRepository = get(), + crypto = get(), + jsonRpcInteractor = get(), + authenticateResponseTopicRepository = get(), + logger = get(named(AndroidCommonDITags.LOGGER)), + getSessionAuthenticateRequest = get(), + metadataStorageRepository = get(), + linkModeStorageRepository = get(), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + ) + } + + single { OnSessionUpdateResponseUseCase(sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } + + single { + OnSessionRequestResponseUseCase( + logger = get(named(AndroidCommonDITags.LOGGER)), + insertEventUseCase = get(), + clientId = get(named(AndroidCommonDITags.CLIENT_ID)), + getSessionRequestByIdUseCase = get() + ) + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/SignDITags.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/SignDITags.kt new file mode 100644 index 000000000..35cef9fac --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/SignDITags.kt @@ -0,0 +1,8 @@ +@file:JvmSynthetic + +package com.reown.sign.di + +internal enum class SignDITags { + SERIALIZER_SET, + DESERIALIZER_MAP +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/SignJsonRpcModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/SignJsonRpcModule.kt new file mode 100644 index 000000000..0e9c2a35b --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/SignJsonRpcModule.kt @@ -0,0 +1,40 @@ +@file:JvmSynthetic + +package com.reown.sign.di + +import com.reown.sign.common.adapters.SessionEventVOJsonAdapter +import com.reown.sign.common.adapters.SessionRequestVOJsonAdapter +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionEventVO +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.utils.addDeserializerEntry +import com.reown.utils.addJsonAdapter +import com.reown.utils.addSerializerEntry +import org.koin.dsl.module + +@JvmSynthetic +internal fun signJsonRpcModule() = module { + addSerializerEntry(SignRpc.SessionPropose::class) + addSerializerEntry(SignRpc.SessionPing::class) + addSerializerEntry(SignRpc.SessionEvent::class) + addSerializerEntry(SignRpc.SessionUpdate::class) + addSerializerEntry(SignRpc.SessionRequest::class) + addSerializerEntry(SignRpc.SessionDelete::class) + addSerializerEntry(SignRpc.SessionSettle::class) + addSerializerEntry(SignRpc.SessionExtend::class) + addSerializerEntry(SignRpc.SessionAuthenticate::class) + + addDeserializerEntry(JsonRpcMethod.WC_SESSION_PROPOSE, SignRpc.SessionPropose::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_SETTLE, SignRpc.SessionSettle::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_REQUEST, SignRpc.SessionRequest::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_DELETE, SignRpc.SessionDelete::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_PING, SignRpc.SessionPing::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_EVENT, SignRpc.SessionEvent::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_UPDATE, SignRpc.SessionUpdate::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_EXTEND, SignRpc.SessionExtend::class) + addDeserializerEntry(JsonRpcMethod.WC_SESSION_AUTHENTICATE, SignRpc.SessionAuthenticate::class) + + addJsonAdapter(SessionEventVO::class.java, ::SessionEventVOJsonAdapter) + addJsonAdapter(SessionRequestVO::class.java, ::SessionRequestVOJsonAdapter) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/di/StorageModule.kt b/protocol/sign/src/main/kotlin/com/reown/sign/di/StorageModule.kt new file mode 100644 index 000000000..5f5637e0e --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/di/StorageModule.kt @@ -0,0 +1,139 @@ +@file:JvmSynthetic + +package com.reown.sign.di + +import com.reown.android.di.sdkBaseStorageModule +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.di.deleteDatabase +import com.reown.sign.SignDatabase +import com.reown.sign.storage.authenticate.AuthenticateResponseTopicRepository +import com.reown.sign.storage.data.dao.namespace.NamespaceDao +import com.reown.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDao +import com.reown.sign.storage.data.dao.proposal.ProposalDao +import com.reown.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDao +import com.reown.sign.storage.data.dao.session.SessionDao +import com.reown.sign.storage.data.dao.temp.TempNamespaceDao +import com.reown.sign.storage.link_mode.LinkModeStorageRepository +import com.reown.sign.storage.proposal.ProposalStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.launch +import org.koin.core.module.Module +import org.koin.core.qualifier.named +import org.koin.core.scope.Scope +import org.koin.dsl.module +import com.reown.android.internal.common.scope as wcScope + +@JvmSynthetic +internal fun storageModule(dbName: String): Module = module { + includes(sdkBaseStorageModule(SignDatabase.Schema, dbName)) + + fun Scope.createSignDB(): SignDatabase = SignDatabase( + driver = get(named(dbName)), + NamespaceDaoAdapter = NamespaceDao.Adapter( + accountsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) + ), + TempNamespaceDaoAdapter = TempNamespaceDao.Adapter( + accountsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) + ), + ProposalNamespaceDaoAdapter = ProposalNamespaceDao.Adapter( + chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) + ), + OptionalNamespaceDaoAdapter = OptionalNamespaceDao.Adapter( + chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), + eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) + ), + SessionDaoAdapter = SessionDao.Adapter( + propertiesAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_MAP)), + transport_typeAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_TRANSPORT_TYPE)) + ), + ProposalDaoAdapter = ProposalDao.Adapter( + propertiesAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_MAP)), + iconsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) + ) + ) + + single { + try { + createSignDB().also { signDatabase -> + wcScope.launch { + try { + signDatabase.sessionDaoQueries.lastInsertedRow().executeAsOneOrNull() + } catch (e: Exception) { + deleteDatabase(dbName) + createSignDB() + } + } + } + } catch (e: Exception) { + deleteDatabase(dbName) + createSignDB() + } + } + + single { + get().sessionDaoQueries + } + + single { + get().namespaceDaoQueries + } + + single { + get().tempNamespaceDaoQueries + } + + single { + get().proposalNamespaceDaoQueries + } + + single { + get().optionalNamespaceDaoQueries + } + + single { + get().proposalDaoQueries + } + + single { + get().authenticateResponseTopicDaoQueries + } + + single { + get().linkModeDaoQueries + } + + single { + SessionStorageRepository( + sessionDaoQueries = get(), + namespaceDaoQueries = get(), + requiredNamespaceDaoQueries = get(), + optionalNamespaceDaoQueries = get(), + tempNamespaceDaoQueries = get() + ) + } + + single { + ProposalStorageRepository( + proposalDaoQueries = get(), + requiredNamespaceDaoQueries = get(), + optionalNamespaceDaoQueries = get() + ) + } + + single { + AuthenticateResponseTopicRepository(authenticateResponseTopicDaoQueries = get()) + } + + single { + LinkModeStorageRepository(linkModeDaoQueries = get()) + } +} diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/SessionRequestQueue.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/SessionRequestQueue.kt new file mode 100644 index 000000000..3d680aff2 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/SessionRequestQueue.kt @@ -0,0 +1,6 @@ +package com.reown.sign.engine + +import com.reown.sign.engine.model.EngineDO +import java.util.concurrent.ConcurrentLinkedQueue + +internal val sessionRequestEventsQueue = ConcurrentLinkedQueue() \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/domain/SignEngine.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/domain/SignEngine.kt similarity index 75% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/domain/SignEngine.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/domain/SignEngine.kt index f117e1b05..f9f571ab9 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/domain/SignEngine.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/domain/SignEngine.kt @@ -1,80 +1,80 @@ @file:JvmSynthetic -package com.walletconnect.sign.engine.domain +package com.reown.sign.engine.domain -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.Trace -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.android.relay.WSSConnectionState -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.engine.model.mapper.toExpiredProposal -import com.walletconnect.sign.engine.model.mapper.toExpiredSessionRequest -import com.walletconnect.sign.engine.model.mapper.toSessionRequest -import com.walletconnect.sign.engine.sessionRequestEventsQueue -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.DisconnectSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.EmitEventUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ExtendSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.FormatAuthenticateMessageUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetListOfVerifyContextsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetPairingsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetSessionProposalsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetSessionsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetVerifyContextByIdUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.PairUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.PingUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ProposeSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RejectSessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RejectSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RespondSessionRequestUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionRequestUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionUpdateUseCaseInterface -import com.walletconnect.sign.engine.use_case.requests.OnPingUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionDeleteUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionEventUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionExtendUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionProposalUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionRequestUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionSettleUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionUpdateUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionAuthenticateResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionProposalResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionRequestResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionSettleResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionUpdateResponseUseCase -import com.walletconnect.sign.json_rpc.domain.DeleteRequestByIdUseCase -import com.walletconnect.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopicInterface -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCaseInterface -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionRequests -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.storage.authenticate.AuthenticateResponseTopicRepository -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.Empty -import com.walletconnect.utils.isSequenceValid +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.Trace +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.android.relay.WSSConnectionState +import com.reown.android.verify.model.VerifyContext +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.engine.model.mapper.toExpiredProposal +import com.reown.sign.engine.model.mapper.toExpiredSessionRequest +import com.reown.sign.engine.model.mapper.toSessionRequest +import com.reown.sign.engine.sessionRequestEventsQueue +import com.reown.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.ApproveSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.DisconnectSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.EmitEventUseCaseInterface +import com.reown.sign.engine.use_case.calls.ExtendSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.FormatAuthenticateMessageUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetListOfVerifyContextsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetPairingsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetSessionProposalsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetSessionsUseCaseInterface +import com.reown.sign.engine.use_case.calls.GetVerifyContextByIdUseCaseInterface +import com.reown.sign.engine.use_case.calls.PairUseCaseInterface +import com.reown.sign.engine.use_case.calls.PingUseCaseInterface +import com.reown.sign.engine.use_case.calls.ProposeSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.RejectSessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.RejectSessionUseCaseInterface +import com.reown.sign.engine.use_case.calls.RespondSessionRequestUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionAuthenticateUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionRequestUseCaseInterface +import com.reown.sign.engine.use_case.calls.SessionUpdateUseCaseInterface +import com.reown.sign.engine.use_case.requests.OnPingUseCase +import com.reown.sign.engine.use_case.requests.OnSessionAuthenticateUseCase +import com.reown.sign.engine.use_case.requests.OnSessionDeleteUseCase +import com.reown.sign.engine.use_case.requests.OnSessionEventUseCase +import com.reown.sign.engine.use_case.requests.OnSessionExtendUseCase +import com.reown.sign.engine.use_case.requests.OnSessionProposalUseCase +import com.reown.sign.engine.use_case.requests.OnSessionRequestUseCase +import com.reown.sign.engine.use_case.requests.OnSessionSettleUseCase +import com.reown.sign.engine.use_case.requests.OnSessionUpdateUseCase +import com.reown.sign.engine.use_case.responses.OnSessionAuthenticateResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionProposalResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionRequestResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionSettleResponseUseCase +import com.reown.sign.engine.use_case.responses.OnSessionUpdateResponseUseCase +import com.reown.sign.json_rpc.domain.DeleteRequestByIdUseCase +import com.reown.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopicInterface +import com.reown.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCaseInterface +import com.reown.sign.json_rpc.domain.GetPendingSessionRequests +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.storage.authenticate.AuthenticateResponseTopicRepository +import com.reown.sign.storage.proposal.ProposalStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.Empty +import com.reown.utils.isSequenceValid import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -83,7 +83,6 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge @@ -216,22 +215,23 @@ internal class SignEngine( } private fun handleRelayRequestsAndResponses() { - jsonRpcInteractor.wssConnectionState - .filterIsInstance() + jsonRpcInteractor.onResubscribe .onEach { - supervisorScope { - launch(Dispatchers.IO) { - resubscribeToSession() - resubscribeToPendingAuthenticateTopics() + scope.launch { + supervisorScope { + launch(Dispatchers.IO) { + resubscribeToSession() + resubscribeToPendingAuthenticateTopics() + } } - } - if (jsonRpcRequestsJob == null) { - jsonRpcRequestsJob = collectJsonRpcRequests() - } + if (jsonRpcRequestsJob == null) { + jsonRpcRequestsJob = collectJsonRpcRequests() + } - if (jsonRpcResponsesJob == null) { - jsonRpcResponsesJob = collectJsonRpcResponses() + if (jsonRpcResponsesJob == null) { + jsonRpcResponsesJob = collectJsonRpcResponses() + } } }.launchIn(scope) } @@ -325,9 +325,7 @@ internal class SignEngine( listOfExpiredSession .map { session -> session.topic } .onEach { sessionTopic -> - runCatching { - crypto.removeKeys(sessionTopic.value) - }.onFailure { logger.error(it) } + runCatching { crypto.removeKeys(sessionTopic.value) }.onFailure { logger.error(it) } sessionStorageRepository.deleteSession(sessionTopic) } diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/EngineDO.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/EngineDO.kt new file mode 100644 index 000000000..7cad80060 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/EngineDO.kt @@ -0,0 +1,245 @@ +@file:JvmSynthetic + +package com.reown.sign.engine.model + +import com.squareup.moshi.JsonClass +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.Validation +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.Sequence +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.foundation.common.model.Topic +import java.net.URI +import com.reown.android.internal.common.model.RelayProtocolOptions as CoreRelayProtocolOptions + +internal sealed class EngineDO { + + class WalletConnectUri( + val topic: Topic, + val symKey: SymmetricKey, + val relay: CoreRelayProtocolOptions, + val version: String = "2", + ) : EngineDO() + + data class SessionProposalEvent( + val proposal: SessionProposal, + val context: VerifyContext + ) : EngineDO(), EngineEvent + + data class SessionRequestEvent( + val request: SessionRequest, + val context: VerifyContext + ) : EngineDO(), EngineEvent + + data class SessionAuthenticateEvent( + val id: Long, + val pairingTopic: String, + val payloadParams: PayloadParams, + val participant: Participant, + val expiryTimestamp: Long, + val verifyContext: VerifyContext + ) : EngineDO(), EngineEvent + + data class PayloadParams( + val chains: List, + val domain: String, + val nonce: String, + val aud: String, + val type: String?, + val iat: String, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + var resources: List?, + val version: String + ) : EngineDO() + + data class Authenticate( + val pairingTopic: String? = null, + val chains: List, + val domain: String, + val nonce: String, + val aud: String, + val type: String?, + val nbf: String?, + val exp: String?, + val statement: String?, + val requestId: String?, + var resources: List?, + val methods: List?, + val expiry: Long? + ) : EngineDO() + + data class Participant( + val publicKey: String, + val metadata: AppMetaData, + ) : EngineDO() + + data class SessionProposal( + val pairingTopic: String, + val name: String, + val description: String, + val url: String, + val icons: List, + val redirect: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map, + val properties: Map?, + val proposerPublicKey: String, + val relayProtocol: String, + val relayData: String?, + ) : EngineDO(), EngineEvent + + data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : EngineDO(), EngineEvent + data class ExpiredRequest(val topic: String, val id: Long) : EngineDO(), EngineEvent + + data class VerifyContext( + val id: Long, + val origin: String, + val validation: Validation, + val verifyUrl: String, + val isScam: Boolean? + ) : EngineDO() + + sealed class Namespace : EngineDO() { + + //Required and Optional + data class Proposal( + val chains: List? = null, + val methods: List, + val events: List + ) : Namespace() + + data class Session( + val chains: List? = null, + val accounts: List, + val methods: List, + val events: List + ) : Namespace() + } + + data class SessionRequest( + val topic: String, + val chainId: String?, + val peerAppMetaData: AppMetaData?, + val request: JSONRPCRequest, + val expiry: Expiry? + ) : EngineDO(), EngineEvent { + + data class JSONRPCRequest( + val id: Long, + val method: String, + val params: String, + ) : EngineDO() + } + + data class SessionPayloadResponse( + val topic: String, + val chainId: String?, + val method: String, + val result: JsonRpcResponse, + ) : EngineDO(), EngineEvent + + data class SessionDelete( + val topic: String, + val reason: String, + ) : EngineDO(), EngineEvent + + data class SessionEvent( + val topic: String, + val name: String, + val data: String, + val chainId: String, //todo: Why was this nullable? + ) : EngineDO(), EngineEvent + + sealed class SessionAuthenticateResponse : EngineDO(), EngineEvent { + data class Result(val id: Long, val cacaos: List, val session: Session?) : SessionAuthenticateResponse() + data class Error(val id: Long, val code: Int, val message: String) : SessionAuthenticateResponse() + } + + sealed class SettledSessionResponse : EngineDO(), EngineEvent { + data class Result(val settledSession: Session) : SettledSessionResponse() + data class Error(val errorMessage: String) : SettledSessionResponse() + } + + sealed class SessionUpdateNamespacesResponse : EngineDO(), EngineEvent { + data class Result(val topic: Topic, val namespaces: Map) : SessionUpdateNamespacesResponse() + data class Error(val errorMessage: String) : SessionUpdateNamespacesResponse() + } + + data class SessionRejected( + val topic: String, + val reason: String, + ) : EngineDO(), EngineEvent + + data class SessionApproved( + val topic: String, + val peerAppMetaData: AppMetaData?, + val accounts: List, + val namespaces: Map, + ) : EngineDO(), EngineEvent + + data class PairingSettle(val topic: Topic, val appMetaData: AppMetaData?) : EngineDO(), EngineEvent + + data class SessionUpdateNamespaces(val topic: Topic, val namespaces: Map) : EngineDO(), EngineEvent + + data class SessionExtend( + override val topic: Topic, + override val expiry: Expiry, + val pairingTopic: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map?, + val namespaces: Map, + val peerAppMetaData: AppMetaData?, + ) : EngineDO(), Sequence, EngineEvent + + data class Session( + override val topic: Topic, + override val expiry: Expiry, + val pairingTopic: String, + val requiredNamespaces: Map, + val optionalNamespaces: Map?, + val namespaces: Map, + val peerAppMetaData: AppMetaData?, + ) : EngineDO(), Sequence, EngineEvent + + data class Event( + val name: String, + val data: String, + val chainId: String, + ) : EngineDO() + + sealed class JsonRpcResponse : EngineDO() { + abstract val id: Long + + @JsonClass(generateAdapter = true) + data class JsonRpcResult( + override val id: Long, + val jsonrpc: String = "2.0", + val result: String, + ) : JsonRpcResponse() + + @JsonClass(generateAdapter = true) + data class JsonRpcError( + override val id: Long, + val jsonrpc: String = "2.0", + val error: Error, + ) : JsonRpcResponse() + + data class Error( + val code: Int, + val message: String, + ) + } + + data class Request( + val topic: String, + val method: String, + val params: String, + val chainId: String, + val expiry: Expiry? = null + ) : EngineDO() +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/ValidationError.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/ValidationError.kt similarity index 92% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/ValidationError.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/model/ValidationError.kt index 2ea785dc7..fcf5b5b48 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/ValidationError.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/ValidationError.kt @@ -1,6 +1,6 @@ -package com.walletconnect.sign.engine.model +package com.reown.sign.engine.model -import com.walletconnect.sign.common.exceptions.* +import com.reown.sign.common.exceptions.* internal sealed class ValidationError(val message: String) { diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/mapper/EngineMapper.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/mapper/EngineMapper.kt new file mode 100644 index 000000000..c988fa822 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/model/mapper/EngineMapper.kt @@ -0,0 +1,359 @@ +@file:JvmSynthetic + +package com.reown.sign.engine.model.mapper + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SessionProposer +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.common.signing.cacao.toCAIP222Message +import com.reown.android.verify.model.VerifyContext +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.exceptions.PeerError +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.common.PayloadParams +import com.reown.sign.common.model.vo.clientsync.common.Requester +import com.reown.sign.common.model.vo.clientsync.common.SessionParticipant +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.ValidationError +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.util.Empty +import java.net.URI +import java.text.SimpleDateFormat +import java.util.Calendar + +@JvmSynthetic +internal fun EngineDO.WalletConnectUri.toAbsoluteString(): String = + "wc:${topic.value}@$version?${getQuery()}&symKey=${symKey.keyAsHex}" + +private fun EngineDO.WalletConnectUri.getQuery(): String { + var query = "relay-protocol=${relay.protocol}" + if (relay.data != null) { + query = "$query&relay-data=${relay.data}" + } + return query +} + +@JvmSynthetic +internal fun SignParams.SessionProposeParams.toEngineDO(topic: Topic): EngineDO.SessionProposal = + EngineDO.SessionProposal( + pairingTopic = topic.value, + name = this.proposer.metadata.name, + description = this.proposer.metadata.description, + url = this.proposer.metadata.url, + icons = this.proposer.metadata.icons.mapNotNull { convertToURI(it) }, + redirect = this.proposer.metadata.redirect?.native ?: String.Empty, + requiredNamespaces = this.requiredNamespaces.toMapOfEngineNamespacesRequired(), + optionalNamespaces = this.optionalNamespaces?.toMapOfEngineNamespacesOptional() ?: emptyMap(), + properties = properties, + proposerPublicKey = this.proposer.publicKey, + relayProtocol = relays.first().protocol, + relayData = relays.first().data + ) + +@JvmSynthetic +internal fun SignParams.SessionProposeParams.toVO(topic: Topic, requestId: Long): ProposalVO = + ProposalVO( + requestId = requestId, + pairingTopic = topic, + name = proposer.metadata.name, + description = proposer.metadata.description, + url = proposer.metadata.url, + icons = proposer.metadata.icons, + redirect = proposer.metadata.redirect?.native ?: String.Empty, + requiredNamespaces = requiredNamespaces, + optionalNamespaces = optionalNamespaces ?: emptyMap(), + properties = properties, + proposerPublicKey = proposer.publicKey, + relayProtocol = relays.first().protocol, + relayData = relays.first().data, + expiry = if (expiryTimestamp != null) Expiry(expiryTimestamp) else null + ) + +@JvmSynthetic +internal fun ProposalVO.toSessionProposeRequest(): WCRequest = + WCRequest( + topic = pairingTopic, + id = requestId, + method = JsonRpcMethod.WC_SESSION_PROPOSE, + params = SignParams.SessionProposeParams( + relays = listOf(RelayProtocolOptions(protocol = relayProtocol, data = relayData)), + proposer = SessionProposer(proposerPublicKey, AppMetaData(name = name, description = description, url = url, icons = icons)), + requiredNamespaces = requiredNamespaces, optionalNamespaces = optionalNamespaces, properties = properties, expiryTimestamp = expiry?.seconds + ), + transportType = TransportType.RELAY + ) + +@JvmSynthetic +internal fun SignParams.SessionRequestParams.toEngineDO( + request: WCRequest, + peerAppMetaData: AppMetaData?, +): EngineDO.SessionRequest = + EngineDO.SessionRequest( + topic = request.topic.value, + chainId = chainId, + peerAppMetaData = peerAppMetaData, + request = EngineDO.SessionRequest.JSONRPCRequest( + id = request.id, + method = this.request.method, + params = this.request.params + ), + if (this.request.expiryTimestamp != null) Expiry(this.request.expiryTimestamp) else null + ) + +@JvmSynthetic +internal fun SignParams.DeleteParams.toEngineDO(topic: Topic): EngineDO.SessionDelete = + EngineDO.SessionDelete(topic.value, message) + +@JvmSynthetic +internal fun SignParams.EventParams.toEngineDO(topic: Topic): EngineDO.SessionEvent = + EngineDO.SessionEvent(topic.value, event.name, event.data.toString(), chainId) + +@JvmSynthetic +internal fun SessionVO.toEngineDO(): EngineDO.Session = + EngineDO.Session( + topic, + expiry, + pairingTopic, + requiredNamespaces.toMapOfEngineNamespacesRequired(), + optionalNamespaces?.toMapOfEngineNamespacesOptional(), + sessionNamespaces.toMapOfEngineNamespacesSession(), + peerAppMetaData + ) + +@JvmSynthetic +internal fun SessionVO.toEngineDOSessionExtend(expiryVO: Expiry): EngineDO.SessionExtend = + EngineDO.SessionExtend( + topic, + expiryVO, + pairingTopic, + requiredNamespaces.toMapOfEngineNamespacesRequired(), + optionalNamespaces?.toMapOfEngineNamespacesOptional(), + sessionNamespaces.toMapOfEngineNamespacesSession(), + selfAppMetaData + ) + +@JvmSynthetic +internal fun SessionVO.toSessionApproved(): EngineDO.SessionApproved = + EngineDO.SessionApproved( + topic = topic.value, + peerAppMetaData = peerAppMetaData, + accounts = sessionNamespaces.flatMap { (_, namespace) -> namespace.accounts }, + namespaces = sessionNamespaces.toMapOfEngineNamespacesSession() + ) + +@JvmSynthetic +internal fun ProposalVO.toSessionSettleParams( + selfParticipant: SessionParticipant, + sessionExpiry: Long, + namespaces: Map, +): SignParams.SessionSettleParams = + SignParams.SessionSettleParams( + relay = RelayProtocolOptions(relayProtocol, relayData), + controller = selfParticipant, + namespaces = namespaces.toMapOfNamespacesVOSession(), + expiry = sessionExpiry, + properties = properties + ) + +@JvmSynthetic +internal fun toSessionProposeParams( + relays: List?, + requiredNamespaces: Map, + optionalNamespaces: Map, + properties: Map?, + selfPublicKey: PublicKey, + appMetaData: AppMetaData, + expiry: Expiry +) = SignParams.SessionProposeParams( + relays = relays ?: listOf(RelayProtocolOptions()), + proposer = SessionProposer(selfPublicKey.keyAsHex, appMetaData), + requiredNamespaces = requiredNamespaces.toNamespacesVORequired(), + optionalNamespaces = optionalNamespaces.toNamespacesVOOptional(), + properties = properties, + expiryTimestamp = expiry.seconds +) + +@JvmSynthetic +internal fun ProposalVO.toEngineDO(): EngineDO.SessionProposal = + EngineDO.SessionProposal( + pairingTopic = pairingTopic.value, + name = name, + description = description, + url = url, + icons = icons.mapNotNull { convertToURI(it) }, + redirect = redirect, + relayData = relayData, + relayProtocol = relayProtocol, + requiredNamespaces = requiredNamespaces.toMapOfEngineNamespacesRequired(), + optionalNamespaces = optionalNamespaces.toMapOfEngineNamespacesOptional(), + proposerPublicKey = proposerPublicKey, + properties = properties + ) + +@JvmSynthetic +internal fun ProposalVO.toExpiredProposal(): EngineDO.ExpiredProposal = EngineDO.ExpiredProposal(pairingTopic.value, proposerPublicKey) + +@JvmSynthetic +internal fun Request.toExpiredSessionRequest() = EngineDO.ExpiredRequest(topic.value, id) + +private fun convertToURI(it: String) = try { + URI(it) +} catch (e: Exception) { + null +} + +@JvmSynthetic +internal fun Map.toNamespacesVORequired(): Map = + this.mapValues { (_, namespace) -> + Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) + } + +@JvmSynthetic +internal fun Map.toNamespacesVOOptional(): Map = + this.mapValues { (_, namespace) -> + Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesRequired(): Map = + this.mapValues { (_, namespace) -> + EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesOptional(): Map = + this.mapValues { (_, namespace) -> + EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun Map.toMapOfEngineNamespacesSession(): Map = + this.mapValues { (_, namespaceVO) -> + EngineDO.Namespace.Session(namespaceVO.chains, namespaceVO.accounts, namespaceVO.methods, namespaceVO.events) + } + +@JvmSynthetic +internal fun Map.toMapOfNamespacesVOSession(): Map = + this.mapValues { (_, namespace) -> + Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) + } + +@JvmSynthetic +internal fun JsonRpcResponse.JsonRpcResult.toEngineDO(): EngineDO.JsonRpcResponse.JsonRpcResult = + EngineDO.JsonRpcResponse.JsonRpcResult(id = id, result = result.toString()) + +@JvmSynthetic +internal fun JsonRpcResponse.JsonRpcError.toEngineDO(): EngineDO.JsonRpcResponse.JsonRpcError = + EngineDO.JsonRpcResponse.JsonRpcError(id = id, error = EngineDO.JsonRpcResponse.Error(error.code, error.message)) + +@JvmSynthetic +internal fun ProposalVO.toSessionApproveParams(selfPublicKey: PublicKey): CoreSignParams.ApprovalParams = + CoreSignParams.ApprovalParams( + relay = RelayProtocolOptions(relayProtocol, relayData), + responderPublicKey = selfPublicKey.keyAsHex + ) + +@JvmSynthetic +internal fun SignParams.SessionRequestParams.toEngineDO(topic: Topic): EngineDO.Request = + EngineDO.Request(topic.value, request.method, request.params, chainId) + +@JvmSynthetic +internal fun SignParams.EventParams.toEngineDOEvent(): EngineDO.Event = + EngineDO.Event(event.name, event.data.toString(), chainId) + +@JvmSynthetic +internal fun Request.toSessionRequest(peerAppMetaData: AppMetaData?): EngineDO.SessionRequest = + EngineDO.SessionRequest(topic.value, chainId, peerAppMetaData, EngineDO.SessionRequest.JSONRPCRequest(id, method, params), expiry) + + +@JvmSynthetic +internal fun ValidationError.toPeerError() = when (this) { + is ValidationError.UnsupportedNamespaceKey -> PeerError.CAIP25.UnsupportedNamespaceKey(message) + is ValidationError.UnsupportedChains -> PeerError.CAIP25.UnsupportedChains(message) + is ValidationError.InvalidEvent -> PeerError.Invalid.Event(message) + is ValidationError.InvalidExtendRequest -> PeerError.Invalid.ExtendRequest(message) + is ValidationError.InvalidSessionRequest -> PeerError.Invalid.Method(message) + is ValidationError.UnauthorizedEvent -> PeerError.Unauthorized.Event(message) + is ValidationError.UnauthorizedMethod -> PeerError.Unauthorized.Method(message) + is ValidationError.UserRejected -> PeerError.CAIP25.UserRejected(message) + is ValidationError.UserRejectedEvents -> PeerError.CAIP25.UserRejectedEvents(message) + is ValidationError.UserRejectedMethods -> PeerError.CAIP25.UserRejectedMethods(message) + is ValidationError.UserRejectedChains -> PeerError.CAIP25.UserRejectedChains(message) + is ValidationError.InvalidSessionProperties -> PeerError.CAIP25.InvalidSessionPropertiesObject(message) + is ValidationError.EmptyNamespaces -> PeerError.CAIP25.EmptySessionNamespaces(message) +} + +@JvmSynthetic +internal fun VerifyContext.toEngineDO(): EngineDO.VerifyContext = + EngineDO.VerifyContext(id, origin, validation, verifyUrl, isScam) + +@JvmSynthetic +internal fun Requester.toEngineDO(): EngineDO.Participant = + EngineDO.Participant(publicKey, metadata) + +@JvmSynthetic +internal fun EngineDO.Authenticate.toCommon(): PayloadParams = + PayloadParams( + domain = domain, + aud = aud, + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + chains = chains, + type = type ?: CacaoType.EIP4361.header, + version = "1", + iat = SimpleDateFormat(Cacao.Payload.ISO_8601_PATTERN).format(Calendar.getInstance().time) + ) + +@JvmSynthetic +internal fun PayloadParams.toEngineDO(): EngineDO.PayloadParams = + EngineDO.PayloadParams( + domain = domain, + aud = aud, + version = "1", + nonce = nonce, + nbf = nbf, + exp = exp, + statement = statement, + requestId = requestId, + resources = resources, + chains = chains, + type = type, + iat = iat + ) + +@JvmSynthetic +internal fun EngineDO.PayloadParams.toCacaoPayload(iss: Issuer): Cacao.Payload = + Cacao.Payload( + iss.value, + domain = domain, + aud = aud, + version = version, + nonce = nonce, + nbf = nbf, + exp = exp, + iat = iat, + statement = statement, + requestId = requestId, + resources = resources + ) + +@JvmSynthetic +internal fun EngineDO.PayloadParams.toCAIP222Message(iss: Issuer, chainName: String): String = + this.toCacaoPayload(iss).toCAIP222Message(chainName) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt similarity index 78% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt index c8bceaf41..dc6781350 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCase.kt @@ -1,50 +1,50 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.NoInternetConnectionException -import com.walletconnect.android.internal.common.exception.NoRelayConnectionException -import com.walletconnect.android.internal.common.exception.RequestExpiredException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.Participant -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.signing.cacao.getChains -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.Trace -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.MissingSessionAuthenticateRequest -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.NoInternetConnectionException +import com.reown.android.internal.common.exception.NoRelayConnectionException +import com.reown.android.internal.common.exception.RequestExpiredException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.Participant +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.common.signing.cacao.getChains +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.CoreValidator +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.Trace +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.MissingSessionAuthenticateRequest +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest +import com.reown.sign.storage.sequence.SessionStorageRepository import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCase.kt similarity index 77% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCase.kt index 897c42332..ee94a5c1b 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCase.kt @@ -1,42 +1,42 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.NoInternetConnectionException -import com.walletconnect.android.internal.common.exception.NoRelayConnectionException -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.ACTIVE_SESSION -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.Trace -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.common.exceptions.SessionProposalExpiredException -import com.walletconnect.sign.common.model.vo.clientsync.common.SessionParticipant -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toMapOfNamespacesVOSession -import com.walletconnect.sign.engine.model.mapper.toSessionApproveParams -import com.walletconnect.sign.engine.model.mapper.toSessionProposeRequest -import com.walletconnect.sign.engine.model.mapper.toSessionSettleParams -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.NoInternetConnectionException +import com.reown.android.internal.common.exception.NoRelayConnectionException +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.ACTIVE_SESSION +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.Trace +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.common.exceptions.SessionProposalExpiredException +import com.reown.sign.common.model.vo.clientsync.common.SessionParticipant +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toMapOfNamespacesVOSession +import com.reown.sign.engine.model.mapper.toSessionApproveParams +import com.reown.sign.engine.model.mapper.toSessionProposeRequest +import com.reown.sign.engine.model.mapper.toSessionSettleParams +import com.reown.sign.storage.proposal.ProposalStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt new file mode 100644 index 000000000..42c0e6661 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt @@ -0,0 +1,92 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.Core +import com.reown.android.internal.common.crypto.codec.Codec +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.sync.ClientJsonRpc +import com.reown.android.internal.common.model.type.ClientParams +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.push_messages.PushMessagesRepository +import com.reown.android.push.notifications.DecryptMessageUseCaseInterface +import com.reown.android.utils.toClient +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.exceptions.InvalidSignParamsType +import com.reown.sign.common.model.vo.clientsync.common.PayloadParams +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import org.bouncycastle.util.encoders.Base64 + +internal class DecryptSignMessageUseCase( + private val codec: Codec, + private val serializer: JsonRpcSerializer, + private val metadataRepository: MetadataStorageRepositoryInterface, + private val pushMessageStorage: PushMessagesRepository +) : DecryptMessageUseCaseInterface { + override suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) { + try { + if (!pushMessageStorage.doesPushMessageExist(sha256(message.toByteArray()))) { + val decryptedMessageString = codec.decrypt(Topic(topic), Base64.decode(message)) + val clientJsonRpc: ClientJsonRpc = serializer.tryDeserialize(decryptedMessageString) ?: return onFailure(InvalidSignParamsType()) + val params: ClientParams = serializer.deserialize(clientJsonRpc.method, decryptedMessageString) ?: return onFailure(InvalidSignParamsType()) + val metadata: AppMetaData = metadataRepository.getByTopicAndType(Topic(topic), AppMetaDataType.PEER) ?: return onFailure(InvalidSignParamsType()) + + when (params) { + is SignParams.SessionProposeParams -> onSuccess(params.toCore(clientJsonRpc.id, topic)) + is SignParams.SessionRequestParams -> onSuccess(params.toCore(clientJsonRpc.id, topic, metadata)) + is SignParams.SessionAuthenticateParams -> onSuccess(params.toCore(clientJsonRpc.id, topic, metadata)) + else -> onFailure(InvalidSignParamsType()) + } + } + } catch (e: Exception) { + onFailure(e) + } + } + + private companion object { + fun SignParams.SessionProposeParams.toCore(id: Long, topic: String): Core.Model.Message.SessionProposal = + Core.Model.Message.SessionProposal( + id, + topic, + proposer.metadata.name, + proposer.metadata.description, + proposer.metadata.url, + proposer.metadata.icons, + proposer.metadata.redirect?.native ?: "", + requiredNamespaces.toCore(), + (optionalNamespaces ?: emptyMap()).toCore(), + properties, + proposer.publicKey, + relays.first().protocol, + relays.first().data + ) + + fun SignParams.SessionRequestParams.toCore(id: Long, topic: String, metaData: AppMetaData): Core.Model.Message.SessionRequest = + Core.Model.Message.SessionRequest( + topic, + chainId, + metaData.toClient(), + Core.Model.Message.SessionRequest.JSONRPCRequest(id, request.method, request.params) + ) + + fun SignParams.SessionAuthenticateParams.toCore(id: Long, topic: String, metaData: AppMetaData): Core.Model.Message.SessionAuthenticate = + Core.Model.Message.SessionAuthenticate( + id, + topic, + metaData.toClient(), + authPayload.toClient(), + expiryTimestamp + ) + + fun PayloadParams.toClient(): Core.Model.Message.SessionAuthenticate.PayloadParams { + return with(this) { + Core.Model.Message.SessionAuthenticate.PayloadParams(chains, domain, nonce, aud, type, nbf, exp, statement, requestId, resources, iat) + } + } + + fun Map.toCore(): Map = + mapValues { (_, namespace) -> Core.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCase.kt new file mode 100644 index 000000000..561be2855 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCase.kt @@ -0,0 +1,51 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.exception.Reason +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class DisconnectSessionUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger, +) : DisconnectSessionUseCaseInterface { + override suspend fun disconnect(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + if (!sessionStorageRepository.isSessionValid(Topic(topic))) { + logger.error("Sending session disconnect error: invalid session $topic") + return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) + } + + val deleteParams = SignParams.DeleteParams(Reason.UserDisconnected.code, Reason.UserDisconnected.message) + val sessionDelete = SignRpc.SessionDelete(params = deleteParams) + val irnParams = IrnParams(Tags.SESSION_DELETE, Ttl(dayInSeconds)) + + logger.log("Sending session disconnect on topic: $topic") + jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, sessionDelete, + onSuccess = { + logger.log("Disconnect sent successfully on topic: $topic") + sessionStorageRepository.deleteSession(Topic(topic)) + jsonRpcInteractor.unsubscribe(Topic(topic)) + onSuccess() + }, + onFailure = { error -> + logger.error("Sending session disconnect error: $error on topic: $topic") + onFailure(error) + } + ) + } +} + +internal interface DisconnectSessionUseCaseInterface { + suspend fun disconnect(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCase.kt new file mode 100644 index 000000000..34bd4df73 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCase.kt @@ -0,0 +1,84 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidEventException +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.common.exceptions.UNAUTHORIZED_EMIT_MESSAGE +import com.reown.sign.common.exceptions.UnauthorizedEventException +import com.reown.sign.common.exceptions.UnauthorizedPeerException +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionEventVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.util.generateId +import kotlinx.coroutines.supervisorScope + +internal class EmitEventUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger, +) : EmitEventUseCaseInterface { + + override suspend fun emit(topic: String, event: EngineDO.Event, id: Long?, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + runCatching { validate(topic, event) }.fold( + onSuccess = { + val eventParams = SignParams.EventParams(SessionEventVO(event.name, event.data), event.chainId) + val sessionEvent = SignRpc.SessionEvent(id = id ?: generateId(), params = eventParams) + val irnParams = IrnParams(Tags.SESSION_EVENT, Ttl(fiveMinutesInSeconds), true) + + logger.log("Emitting event on topic: $topic") + jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, sessionEvent, + onSuccess = { + logger.log("Event sent successfully, on topic: $topic") + onSuccess() + }, + onFailure = { error -> + logger.error("Sending event error: $error, on topic: $topic") + onFailure(error) + } + ) + }, + onFailure = { error -> + logger.error("Sending event error: $error, on topic: $topic") + onFailure(error) + } + ) + } + + private fun validate(topic: String, event: EngineDO.Event) { + if (!sessionStorageRepository.isSessionValid(Topic(topic))) { + logger.error("Emit - cannot find sequence for topic: $topic") + throw CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic") + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) + if (!session.isSelfController) { + logger.error("Emit - unauthorized peer: $topic") + throw UnauthorizedPeerException(UNAUTHORIZED_EMIT_MESSAGE) + } + + SignValidator.validateEvent(event) { error -> + logger.error("Emit - invalid event: $topic") + throw InvalidEventException(error.message) + } + + val namespaces = session.sessionNamespaces + SignValidator.validateChainIdWithEventAuthorisation(event.chainId, event.name, namespaces) { error -> + logger.error("Emit - unauthorized event: $topic") + throw UnauthorizedEventException(error.message) + } + } +} + +internal interface EmitEventUseCaseInterface { + suspend fun emit(topic: String, event: EngineDO.Event, id: Long? = null, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCase.kt new file mode 100644 index 000000000..7dd06a9d6 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCase.kt @@ -0,0 +1,58 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.internal.utils.weekInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.common.exceptions.NotSettledSessionException +import com.reown.sign.common.exceptions.SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class ExtendSessionUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger, +) : ExtendSessionUseCaseInterface { + + override suspend fun extend(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + if (!sessionStorageRepository.isSessionValid(Topic(topic))) { + return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) + if (!session.isAcknowledged) { + logger.error("Sending session extend error: not acknowledged session on topic: $topic") + return@supervisorScope onFailure(NotSettledSessionException("$SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE$topic")) + } + + val newExpiration = session.expiry.seconds + weekInSeconds + sessionStorageRepository.extendSession(Topic(topic), newExpiration) + val sessionExtend = SignRpc.SessionExtend(params = SignParams.ExtendParams(newExpiration)) + val irnParams = IrnParams(Tags.SESSION_EXTEND, Ttl(dayInSeconds)) + + logger.log("Sending session extend on topic: $topic") + jsonRpcInteractor.publishJsonRpcRequest( + Topic(topic), irnParams, sessionExtend, + onSuccess = { + logger.log("Session extend sent successfully on topic: $topic") + onSuccess() + }, + onFailure = { error -> + logger.error("Sending session extend error: $error on topic: $topic") + onFailure(error) + }) + } +} + +internal interface ExtendSessionUseCaseInterface { + suspend fun extend(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt similarity index 78% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt index 75f562baa..4af64a247 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/FormatAuthenticateMessageUseCase.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toCAIP222Message +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.utils.CoreValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toCAIP222Message import kotlinx.coroutines.supervisorScope internal class FormatAuthenticateMessageUseCase : FormatAuthenticateMessageUseCaseInterface { diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt new file mode 100644 index 000000000..c805b5ebc --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt @@ -0,0 +1,13 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO + +internal class GetListOfVerifyContextsUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetListOfVerifyContextsUseCaseInterface { + override suspend fun getListOfVerifyContexts(): List = verifyContextStorageRepository.getAll().map { verifyContext -> verifyContext.toEngineDO() } +} + +internal interface GetListOfVerifyContextsUseCaseInterface { + suspend fun getListOfVerifyContexts(): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt new file mode 100644 index 000000000..ee191220b --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt @@ -0,0 +1,18 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.utils.CoreValidator +import com.reown.sign.common.validator.SignValidator + +class GetNamespacesFromReCaps { + operator fun invoke(chains: List, methods: List): Map { + if (!chains.all { chain -> CoreValidator.isChainIdCAIP2Compliant(chain) }) throw Exception("Chains are not CAIP-2 compliant") + if (!chains.all { chain -> SignValidator.getNamespaceKeyFromChainId(chain) == EIP155 }) throw Exception("Only eip155 (EVM) is supported") + val namespace = SignValidator.getNamespaceKeyFromChainId(chains.first()) + return mapOf(namespace to Namespace.Proposal(events = listOf("chainChanged", "accountsChanged"), methods = methods, chains = chains)) + } + + companion object { + private const val EIP155 = "eip155" + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt similarity index 78% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt index 7f67e7a6d..845051fc5 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCase.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.Core -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod +import com.reown.android.Core +import com.reown.android.pairing.client.PairingInterface +import com.reown.sign.json_rpc.model.JsonRpcMethod internal class GetPairingForSessionAuthenticateUseCase(private var pairingProtocol: PairingInterface) { operator fun invoke(pairingTopic: String?): Core.Model.Pairing { diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingsUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingsUseCase.kt new file mode 100644 index 000000000..6f5ab0c1e --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPairingsUseCase.kt @@ -0,0 +1,20 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.model.mapper.toPairing +import com.reown.sign.engine.model.EngineDO +import kotlinx.coroutines.supervisorScope + +internal class GetPairingsUseCase(private val pairingInterface: PairingInterface) : GetPairingsUseCaseInterface { + + override suspend fun getListOfSettledPairings(): List = supervisorScope { + return@supervisorScope pairingInterface.getPairings().map { pairing -> + val mappedPairing = pairing.toPairing() + EngineDO.PairingSettle(mappedPairing.topic, mappedPairing.peerAppMetaData) + } + } +} + +internal interface GetPairingsUseCaseInterface { + suspend fun getListOfSettledPairings(): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt new file mode 100644 index 000000000..a265de1a8 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt @@ -0,0 +1,24 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.json_rpc.model.toRequest + +internal class GetPendingAuthenticateRequestUseCase( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) : GetPendingAuthenticateRequestUseCaseInterface { + override suspend fun getPendingAuthenticateRequests(): List> { + return jsonRpcHistory.getListOfPendingRecords() + .filter { record -> record.method == JsonRpcMethod.WC_SESSION_AUTHENTICATE } + .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } + } +} + +internal interface GetPendingAuthenticateRequestUseCaseInterface { + suspend fun getPendingAuthenticateRequests(): List> +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt new file mode 100644 index 000000000..405de4951 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt @@ -0,0 +1,19 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.storage.proposal.ProposalStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class GetSessionProposalsUseCase(private val proposalStorageRepository: ProposalStorageRepository) : GetSessionProposalsUseCaseInterface { + override suspend fun getSessionProposals(): List = + supervisorScope { + proposalStorageRepository.getProposals().filter { proposal -> proposal.expiry?.let { !it.isExpired() } ?: true }.map(ProposalVO::toEngineDO) + } +} + +internal interface GetSessionProposalsUseCaseInterface { + suspend fun getSessionProposals(): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionsUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionsUseCase.kt new file mode 100644 index 000000000..3d754f2b4 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetSessionsUseCase.kt @@ -0,0 +1,28 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.isSequenceValid +import kotlinx.coroutines.supervisorScope + +internal class GetSessionsUseCase( + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val selfAppMetaData: AppMetaData +) : GetSessionsUseCaseInterface { + + override suspend fun getListOfSettledSessions(): List = supervisorScope { + return@supervisorScope sessionStorageRepository.getListOfSessionVOsWithoutMetadata() + .filter { session -> session.isAcknowledged && session.expiry.isSequenceValid() } + .map { session -> session.copy(selfAppMetaData = selfAppMetaData, peerAppMetaData = metadataStorageRepository.getByTopicAndType(session.topic, AppMetaDataType.PEER)) } + .map { session -> session.toEngineDO() } + } +} + +internal interface GetSessionsUseCaseInterface { + suspend fun getListOfSettledSessions(): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt new file mode 100644 index 000000000..53b24c95d --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt @@ -0,0 +1,13 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO + +internal class GetVerifyContextByIdUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetVerifyContextByIdUseCaseInterface { + override suspend fun getVerifyContext(id: Long): EngineDO.VerifyContext? = verifyContextStorageRepository.get(id)?.toEngineDO() +} + +internal interface GetVerifyContextByIdUseCaseInterface { + suspend fun getVerifyContext(id: Long): EngineDO.VerifyContext? +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PairUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PairUseCase.kt similarity index 77% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PairUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PairUseCase.kt index aaceafd8f..4c480df16 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PairUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PairUseCase.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.Core -import com.walletconnect.android.pairing.client.PairingInterface +import com.reown.android.Core +import com.reown.android.pairing.client.PairingInterface import kotlinx.coroutines.supervisorScope internal class PairUseCase(private val pairingInterface: PairingInterface) : PairUseCaseInterface { diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PingUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PingUseCase.kt similarity index 79% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PingUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PingUseCase.kt index ba971d3af..805d7a856 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/PingUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/PingUseCase.kt @@ -1,17 +1,17 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.utils.thirtySeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.storage.sequence.SessionStorageRepository import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.filter diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCase.kt new file mode 100644 index 000000000..ce791304e --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCase.kt @@ -0,0 +1,119 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.PROPOSAL_EXPIRY +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.common.exceptions.InvalidPropertiesException +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toNamespacesVOOptional +import com.reown.sign.engine.model.mapper.toNamespacesVORequired +import com.reown.sign.engine.model.mapper.toSessionProposeParams +import com.reown.sign.engine.model.mapper.toVO +import com.reown.sign.storage.proposal.ProposalStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class ProposeSessionUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val crypto: KeyManagementRepository, + private val proposalStorageRepository: ProposalStorageRepository, + private val selfAppMetaData: AppMetaData, + private val logger: Logger +) : ProposeSessionUseCaseInterface { + + override suspend fun proposeSession( + requiredNamespaces: Map?, + optionalNamespaces: Map?, + properties: Map?, + pairing: Pairing, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) = supervisorScope { + val relay = RelayProtocolOptions(pairing.relayProtocol, pairing.relayData) + + runCatching { validate(requiredNamespaces, optionalNamespaces, properties) }.fold( + onSuccess = { + val expiry = Expiry(PROPOSAL_EXPIRY) + val selfPublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() + val sessionProposal: SignParams.SessionProposeParams = + toSessionProposeParams( + listOf(relay), + requiredNamespaces ?: emptyMap(), + optionalNamespaces ?: emptyMap(), + properties, selfPublicKey, selfAppMetaData, expiry + ) + val request = SignRpc.SessionPropose(params = sessionProposal) + proposalStorageRepository.insertProposal(sessionProposal.toVO(pairing.topic, request.id)) + val irnParams = IrnParams(Tags.SESSION_PROPOSE, Ttl(fiveMinutesInSeconds), true) + jsonRpcInteractor.subscribe(pairing.topic) { error -> onFailure(error) } + + logger.log("Sending proposal on topic: ${pairing.topic.value}") + jsonRpcInteractor.publishJsonRpcRequest(pairing.topic, irnParams, request, + onSuccess = { + logger.log("Session proposal sent successfully, topic: ${pairing.topic}") + onSuccess() + }, + onFailure = { error -> + logger.error("Failed to send a session proposal: $error") + onFailure(error) + } + ) + }, + onFailure = { error -> + logger.error("Failed to validate session proposal: $error") + onFailure(error) + } + ) + } + + private fun validate( + requiredNamespaces: Map?, + optionalNamespaces: Map?, + properties: Map? + ) { + requiredNamespaces?.let { namespaces -> + SignValidator.validateProposalNamespaces(namespaces.toNamespacesVORequired()) { error -> + logger.error("Failed to send a session proposal - required namespaces error: $error") + throw InvalidNamespaceException(error.message) + } + } + + optionalNamespaces?.let { namespaces -> + SignValidator.validateProposalNamespaces(namespaces.toNamespacesVOOptional()) { error -> + logger.error("Failed to send a session proposal - optional namespaces error: $error") + throw InvalidNamespaceException(error.message) + } + } + + properties?.let { + SignValidator.validateProperties(properties) { error -> + logger.error("Failed to send a session proposal - session properties error: $error") + throw InvalidPropertiesException(error.message) + } + } + } +} + +internal interface ProposeSessionUseCaseInterface { + suspend fun proposeSession( + requiredNamespaces: Map?, + optionalNamespaces: Map?, + properties: Map?, + pairing: Pairing, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt new file mode 100644 index 000000000..bbc148525 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt @@ -0,0 +1,111 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.RequestExpiredException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Participants +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.MissingSessionAuthenticateRequest +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class RejectSessionAuthenticateUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val getPendingSessionAuthenticateRequest: GetPendingSessionAuthenticateRequest, + private val crypto: KeyManagementRepository, + private val verifyContextStorageRepository: VerifyContextStorageRepository, + private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, + private val insertEventUseCase: InsertEventUseCase, + private val clientId: String, + private val logger: Logger +) : RejectSessionAuthenticateUseCaseInterface { + override suspend fun rejectSessionAuthenticate(id: Long, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + val jsonRpcHistoryEntry = getPendingSessionAuthenticateRequest(id) + if (jsonRpcHistoryEntry == null) { + logger.error(MissingSessionAuthenticateRequest().message) + onFailure(MissingSessionAuthenticateRequest()) + return@supervisorScope + } + + jsonRpcHistoryEntry.expiry?.let { + if (it.isExpired()) { + logger.error("Session Authenticate Request Expired: ${jsonRpcHistoryEntry.topic}, id: ${jsonRpcHistoryEntry.id}") + throw RequestExpiredException("This request has expired, id: ${jsonRpcHistoryEntry.id}") + } + } + + //todo: handle error codes + val response = JsonRpcResponse.JsonRpcError(id, error = JsonRpcResponse.Error(12001, reason)) + val sessionAuthenticateParams: SignParams.SessionAuthenticateParams = jsonRpcHistoryEntry.params + val receiverMetadata: AppMetaData = sessionAuthenticateParams.requester.metadata + val receiverPublicKey = PublicKey(sessionAuthenticateParams.requester.publicKey) + val senderPublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() + val symmetricKey: SymmetricKey = crypto.generateSymmetricKeyFromKeyAgreement(senderPublicKey, receiverPublicKey) + val responseTopic: Topic = crypto.getTopicFromKey(receiverPublicKey) + crypto.setKey(symmetricKey, responseTopic.value) + + if (jsonRpcHistoryEntry.transportType == TransportType.LINK_MODE && receiverMetadata.redirect?.linkMode == true) { + if (receiverMetadata.redirect?.universal.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) + try { + linkModeJsonRpcInteractor.triggerResponse( + responseTopic, + response, + receiverMetadata.redirect?.universal!!, + Participants(senderPublicKey, receiverPublicKey), + EnvelopeType.ONE + ) + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_AUTHENTICATE_LINK_MODE_RESPONSE_REJECT.id.toString(), + Properties(clientId = clientId, correlationId = id, direction = Direction.SENT.state) + ) + ) + } catch (e: Exception) { + onFailure(e) + } + } else { + val irnParams = IrnParams(Tags.SESSION_AUTHENTICATE_RESPONSE_REJECT, Ttl(dayInSeconds), false) + logger.log("Sending Session Authenticate Reject on topic: $responseTopic") + jsonRpcInteractor.publishJsonRpcResponse( + responseTopic, irnParams, response, envelopeType = EnvelopeType.ONE, participants = Participants(senderPublicKey, receiverPublicKey), + onSuccess = { + logger.log("Session Authenticate Reject Responded on topic: $responseTopic") + scope.launch { supervisorScope { verifyContextStorageRepository.delete(id) } } + onSuccess() + }, + onFailure = { error -> + logger.error("Session Authenticate Error Responded on topic: $responseTopic") + scope.launch { supervisorScope { verifyContextStorageRepository.delete(id) } } + onFailure(error) + } + ) + } + } +} + +internal interface RejectSessionAuthenticateUseCaseInterface { + suspend fun rejectSessionAuthenticate(id: Long, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionUseCase.kt new file mode 100644 index 000000000..66ca48653 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionUseCase.kt @@ -0,0 +1,61 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.Core +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.PeerError +import com.reown.sign.common.exceptions.SessionProposalExpiredException +import com.reown.sign.engine.model.mapper.toSessionProposeRequest +import com.reown.sign.storage.proposal.ProposalStorageRepository +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class RejectSessionUseCase( + private val verifyContextStorageRepository: VerifyContextStorageRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val proposalStorageRepository: ProposalStorageRepository, + private val pairingController: PairingControllerInterface, + private val logger: Logger +) : RejectSessionUseCaseInterface { + + override suspend fun reject(proposerPublicKey: String, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + val proposal = proposalStorageRepository.getProposalByKey(proposerPublicKey) + proposal.expiry?.let { + if (it.isExpired()) { + logger.error("Proposal expired on reject, topic: ${proposal.pairingTopic.value}, id: ${proposal.requestId}") + throw SessionProposalExpiredException("Session proposal expired") + } + } + + logger.log("Sending session rejection, topic: ${proposal.pairingTopic.value}") + jsonRpcInteractor.respondWithError( + proposal.toSessionProposeRequest(), + PeerError.EIP1193.UserRejectedRequest(reason), + IrnParams(Tags.SESSION_PROPOSE_RESPONSE_REJECT, Ttl(fiveMinutesInSeconds)), + onSuccess = { + logger.log("Session rejection sent successfully, topic: ${proposal.pairingTopic.value}") + scope.launch { + proposalStorageRepository.deleteProposal(proposerPublicKey) + verifyContextStorageRepository.delete(proposal.requestId) + jsonRpcInteractor.unsubscribe(proposal.pairingTopic) + } + onSuccess() + }, + onFailure = { error -> + logger.error("Session rejection sent failure, topic: ${proposal.pairingTopic.value}. Error: $error") + onFailure(error) + }) + } +} + +internal interface RejectSessionUseCaseInterface { + suspend fun reject(proposerPublicKey: String, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit = {}) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt new file mode 100644 index 000000000..b8f46f92b --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt @@ -0,0 +1,135 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.exception.RequestExpiredException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.engine.sessionRequestEventsQueue +import com.reown.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class RespondSessionRequestUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val getPendingJsonRpcHistoryEntryByIdUseCase: GetPendingJsonRpcHistoryEntryByIdUseCase, + private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, + private val logger: Logger, + private val verifyContextStorageRepository: VerifyContextStorageRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val insertEventUseCase: InsertEventUseCase, + private val clientId: String, +) : RespondSessionRequestUseCaseInterface { + private val _events: MutableSharedFlow = MutableSharedFlow() + override val events: SharedFlow = _events.asSharedFlow() + override suspend fun respondSessionRequest( + topic: String, + jsonRpcResponse: JsonRpcResponse, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) = supervisorScope { + val topicWrapper = Topic(topic) + if (!sessionStorageRepository.isSessionValid(topicWrapper)) { + logger.error("Request response - invalid session: $topic, id: ${jsonRpcResponse.id}") + return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) + } + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(topicWrapper) + .run { + val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) + this.copy(peerAppMetaData = peerAppMetaData) + } + + if (getPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcResponse.id) == null) { + logger.error("Request doesn't exist: $topic, id: ${jsonRpcResponse.id}") + throw RequestExpiredException("This request has expired, id: ${jsonRpcResponse.id}") + } + getPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcResponse.id)?.params?.request?.expiryTimestamp?.let { + if (Expiry(it).isExpired()) { + logger.error("Request Expired: $topic, id: ${jsonRpcResponse.id}") + throw RequestExpiredException("This request has expired, id: ${jsonRpcResponse.id}") + } + } + + if (session.transportType == TransportType.LINK_MODE && session.peerLinkMode == true) { + if (session.peerAppLink.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) + try { + removePendingSessionRequestAndEmit(jsonRpcResponse.id) + linkModeJsonRpcInteractor.triggerResponse(Topic(topic), jsonRpcResponse, session.peerAppLink) + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_REQUEST_LINK_MODE_RESPONSE.id.toString(), + Properties(correlationId = jsonRpcResponse.id, clientId = clientId, direction = Direction.SENT.state) + ) + ) + } catch (e: Exception) { + onFailure(e) + } + } else { + val irnParams = IrnParams(Tags.SESSION_REQUEST_RESPONSE, Ttl(fiveMinutesInSeconds)) + logger.log("Sending session request response on topic: $topic, id: ${jsonRpcResponse.id}") + jsonRpcInteractor.publishJsonRpcResponse(topic = Topic(topic), params = irnParams, response = jsonRpcResponse, + onSuccess = { + onSuccess() + logger.log("Session request response sent successfully on topic: $topic, id: ${jsonRpcResponse.id}") + scope.launch { + supervisorScope { + removePendingSessionRequestAndEmit(jsonRpcResponse.id) + } + } + }, + onFailure = { error -> + logger.error("Sending session response error: $error, id: ${jsonRpcResponse.id}") + onFailure(error) + } + ) + } + } + + private suspend fun removePendingSessionRequestAndEmit(id: Long) { + verifyContextStorageRepository.delete(id) + sessionRequestEventsQueue.find { pendingRequestEvent -> pendingRequestEvent.request.request.id == id }?.let { event -> + sessionRequestEventsQueue.remove(event) + } + if (sessionRequestEventsQueue.isNotEmpty()) { + sessionRequestEventsQueue.find { event -> if (event.request.expiry != null) !event.request.expiry.isExpired() else true }?.let { event -> + _events.emit(event) + } + } + } +} + +internal interface RespondSessionRequestUseCaseInterface { + val events: SharedFlow + suspend fun respondSessionRequest( + topic: String, + jsonRpcResponse: JsonRpcResponse, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt similarity index 79% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt index 758cd0312..7fb792641 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCase.kt @@ -1,45 +1,45 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls import android.util.Base64 -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.InvalidExpiryException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.ATT_KEY -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX -import com.walletconnect.android.internal.common.signing.cacao.mergeReCaps -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.internal.utils.getParticipantTag -import com.walletconnect.android.internal.utils.oneHourInSeconds -import com.walletconnect.android.pairing.model.mapper.toPairing -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.common.Requester -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toCommon -import com.walletconnect.sign.engine.model.mapper.toMapOfEngineNamespacesOptional -import com.walletconnect.sign.storage.authenticate.AuthenticateResponseTopicRepository -import com.walletconnect.sign.storage.link_mode.LinkModeStorageRepository -import com.walletconnect.utils.Empty +import com.reown.android.Core +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.InvalidExpiryException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.signing.cacao.Cacao.Payload.Companion.ATT_KEY +import com.reown.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX +import com.reown.android.internal.common.signing.cacao.mergeReCaps +import com.reown.android.internal.utils.CoreValidator +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.internal.utils.getParticipantTag +import com.reown.android.internal.utils.oneHourInSeconds +import com.reown.android.pairing.model.mapper.toPairing +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.common.Requester +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toCommon +import com.reown.sign.engine.model.mapper.toMapOfEngineNamespacesOptional +import com.reown.sign.storage.authenticate.AuthenticateResponseTopicRepository +import com.reown.sign.storage.link_mode.LinkModeStorageRepository +import com.reown.utils.Empty import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.async import kotlinx.coroutines.launch diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCase.kt new file mode 100644 index 000000000..cb96ca451 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCase.kt @@ -0,0 +1,154 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.exception.InvalidExpiryException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.CoreValidator +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidRequestException +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.common.exceptions.UnauthorizedMethodException +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeout +import java.util.concurrent.TimeUnit + +internal class SessionRequestUseCase( + private val sessionStorageRepository: SessionStorageRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val insertEventUseCase: InsertEventUseCase, + private val clientId: String, + private val logger: Logger, +) : SessionRequestUseCaseInterface { + private val _errors: MutableSharedFlow = MutableSharedFlow() + override val errors: SharedFlow = _errors.asSharedFlow() + + override suspend fun sessionRequest(request: EngineDO.Request, onSuccess: (Long) -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { + if (!sessionStorageRepository.isSessionValid(Topic(request.topic))) { + return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE${request.topic}")) + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(request.topic)) + .run { + val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) + this.copy(peerAppMetaData = peerAppMetaData) + } + + val nowInSeconds = currentTimeInSeconds + if (!CoreValidator.isExpiryWithinBounds(request.expiry)) { + logger.error("Sending session request error: expiry not within bounds") + return@supervisorScope onFailure(InvalidExpiryException()) + } + val expiry = request.expiry ?: Expiry(currentTimeInSeconds + fiveMinutesInSeconds) + SignValidator.validateSessionRequest(request) { error -> + logger.error("Sending session request error: invalid session request, ${error.message}") + return@supervisorScope onFailure(InvalidRequestException(error.message)) + } + + val namespaces: Map = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(request.topic)).sessionNamespaces + SignValidator.validateChainIdWithMethodAuthorisation(request.chainId, request.method, namespaces) { error -> + logger.error("Sending session request error: unauthorized method, ${error.message}") + return@supervisorScope onFailure(UnauthorizedMethodException(error.message)) + } + + val params = SignParams.SessionRequestParams(SessionRequestVO(request.method, request.params, expiry.seconds), request.chainId) + val sessionPayload = SignRpc.SessionRequest(params = params) + + if (session.transportType == TransportType.LINK_MODE && session.peerLinkMode == true) { + if (session.peerAppLink.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) + try { + linkModeJsonRpcInteractor.triggerRequest(sessionPayload, Topic(request.topic), session.peerAppLink) + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_REQUEST_LINK_MODE.id.toString(), + Properties(correlationId = sessionPayload.id, clientId = clientId, direction = Direction.SENT.state) + ) + ) + } catch (e: Exception) { + onFailure(e) + } + } else { + val irnParamsTtl = expiry.run { + val defaultTtl = fiveMinutesInSeconds + val extractedTtl = seconds - nowInSeconds + val newTtl = extractedTtl.takeIf { extractedTtl >= defaultTtl } ?: defaultTtl + + Ttl(newTtl) + } + val irnParams = IrnParams(Tags.SESSION_REQUEST, irnParamsTtl, true) + val requestTtlInSeconds = expiry.run { seconds - nowInSeconds } + + logger.log("Sending session request on topic: ${request.topic}}") + jsonRpcInteractor.publishJsonRpcRequest(Topic(request.topic), irnParams, sessionPayload, + onSuccess = { + logger.log("Session request sent successfully on topic: ${request.topic}") + onSuccess(sessionPayload.id) + scope.launch { + try { + withTimeout(TimeUnit.SECONDS.toMillis(requestTtlInSeconds)) { + collectResponse(sessionPayload.id) { cancel() } + } + } catch (e: TimeoutCancellationException) { + _errors.emit(SDKError(e)) + } + } + }, + onFailure = { error -> + logger.error("Sending session request error: $error") + onFailure(error) + } + ) + } + } + + private suspend fun collectResponse(id: Long, onResponse: (Result) -> Unit = {}) { + jsonRpcInteractor.peerResponse + .filter { response -> response.response.id == id } + .collect { response -> + when (val result = response.response) { + is JsonRpcResponse.JsonRpcResult -> onResponse(Result.success(result)) + is JsonRpcResponse.JsonRpcError -> onResponse(Result.failure(Throwable(result.errorMessage))) + } + } + } +} + +internal interface SessionRequestUseCaseInterface { + val errors: SharedFlow + suspend fun sessionRequest(request: EngineDO.Request, onSuccess: (Long) -> Unit, onFailure: (Throwable) -> Unit) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCase.kt new file mode 100644 index 000000000..7847a8c54 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCase.kt @@ -0,0 +1,102 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.exception.GenericException +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE +import com.reown.sign.common.exceptions.NotSettledSessionException +import com.reown.sign.common.exceptions.SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE +import com.reown.sign.common.exceptions.UNAUTHORIZED_UPDATE_MESSAGE +import com.reown.sign.common.exceptions.UnauthorizedPeerException +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toMapOfNamespacesVOSession +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class SessionUpdateUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger, +) : SessionUpdateUseCaseInterface { + + override suspend fun sessionUpdate( + topic: String, + namespaces: Map, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) = supervisorScope { + runCatching { validate(topic, namespaces) }.fold( + onSuccess = { + val params = SignParams.UpdateNamespacesParams(namespaces.toMapOfNamespacesVOSession()) + val sessionUpdate = SignRpc.SessionUpdate(params = params) + val irnParams = IrnParams(Tags.SESSION_UPDATE, Ttl(dayInSeconds)) + + try { + logger.log("Sending session update on topic: $topic") + sessionStorageRepository.insertTempNamespaces(topic, namespaces.toMapOfNamespacesVOSession(), sessionUpdate.id) + jsonRpcInteractor.publishJsonRpcRequest( + Topic(topic), irnParams, sessionUpdate, + onSuccess = { + logger.log("Update sent successfully, topic: $topic") + onSuccess() + }, + onFailure = { error -> + logger.error("Sending session update error: $error, topic: $topic") + sessionStorageRepository.deleteTempNamespacesByRequestId(sessionUpdate.id) + onFailure(error) + }) + } catch (e: Exception) { + logger.error("Error updating namespaces: $e") + onFailure(GenericException("Error updating namespaces: $e")) + } + }, + onFailure = { + logger.error("Error updating namespaces: $it") + onFailure(it) + } + ) + } + + private fun validate(topic: String, namespaces: Map) { + if (!sessionStorageRepository.isSessionValid(Topic(topic))) { + logger.error("Sending session update error: cannot find sequence for topic: $topic") + throw CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic") + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) + + if (!session.isSelfController) { + logger.error("Sending session update error: unauthorized peer") + throw UnauthorizedPeerException(UNAUTHORIZED_UPDATE_MESSAGE) + } + + if (!session.isAcknowledged) { + logger.error("Sending session update error: session is not acknowledged") + throw NotSettledSessionException("$SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE$topic") + } + + SignValidator.validateSessionNamespace(namespaces.toMapOfNamespacesVOSession(), session.requiredNamespaces) { error -> + logger.error("Sending session update error: invalid namespaces $error") + throw InvalidNamespaceException(error.message) + } + } +} + +internal interface SessionUpdateUseCaseInterface { + suspend fun sessionUpdate( + topic: String, + namespaces: Map, + onSuccess: () -> Unit, + onFailure: (Throwable) -> Unit, + ) +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnPingUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnPingUseCase.kt new file mode 100644 index 000000000..87aff5075 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnPingUseCase.kt @@ -0,0 +1,19 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.thirtySeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import kotlinx.coroutines.supervisorScope + +internal class OnPingUseCase(private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, private val logger: Logger) { + + suspend operator fun invoke(request: WCRequest) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_PING_RESPONSE, Ttl(thirtySeconds)) + logger.log("Session ping received on topic: ${request.topic}") + jsonRpcInteractor.respondWithSuccess(request, irnParams) + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt new file mode 100644 index 000000000..fc4612287 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt @@ -0,0 +1,101 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.Core +import com.reown.android.internal.common.exception.Invalid +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.verify.domain.ResolveAttestationIdUseCase +import com.reown.android.verify.model.VerifyContext +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class OnSessionAuthenticateUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, + private val pairingController: PairingControllerInterface, + private val insertTelemetryEventUseCase: InsertTelemetryEventUseCase, + private val insertEventUseCase: InsertEventUseCase, + private val clientId: String, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, authenticateSessionParams: SignParams.SessionAuthenticateParams) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_AUTHENTICATE_RESPONSE_AUTO_REJECT, Ttl(dayInSeconds)) + logger.log("Received session authenticate: ${request.topic}") + try { + if (Expiry(authenticateSessionParams.expiryTimestamp).isExpired()) { + logger.log("Received session authenticate - expiry error: ${request.topic}") + .also { insertTelemetryEventUseCase(Props(type = EventType.Error.AUTHENTICATED_SESSION_EXPIRED, properties = Properties(topic = request.topic.value))) } + jsonRpcInteractor.respondWithError(request, Invalid.RequestExpired, irnParams) + _events.emit(SDKError(Throwable("Received session authenticate - expiry error: ${request.topic}"))) + return@supervisorScope + } + + val url = authenticateSessionParams.metadataUrl + pairingController.setRequestReceived(Core.Params.RequestReceived(request.topic.value)) + if (request.transportType == TransportType.LINK_MODE) { + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_AUTHENTICATE_LINK_MODE.id.toString(), + Properties(correlationId = request.id, clientId = clientId, direction = Direction.RECEIVED.state) + ) + ) + } + resolveAttestationIdUseCase(request, url, linkMode = authenticateSessionParams.linkMode, appLink = authenticateSessionParams.appLink) { verifyContext -> + emitSessionAuthenticate(request, authenticateSessionParams, verifyContext) + } + } catch (e: Exception) { + logger.log("Received session authenticate - cannot handle request: ${request.topic}") + jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot handle a auth request: ${e.message}, topic: ${request.topic}"), irnParams) + _events.emit(SDKError(e)) + } + } + + private fun emitSessionAuthenticate( + request: WCRequest, + authenticateSessionParams: SignParams.SessionAuthenticateParams, + verifyContext: VerifyContext + ) { + scope.launch { + logger.log("Received session authenticate - emitting: ${request.topic}") + _events.emit( + EngineDO.SessionAuthenticateEvent( + request.id, + request.topic.value, + authenticateSessionParams.authPayload.toEngineDO(), + authenticateSessionParams.requester.toEngineDO(), + authenticateSessionParams.expiryTimestamp, + verifyContext.toEngineDO() + ) + ) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt new file mode 100644 index 000000000..22b70aa01 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt @@ -0,0 +1,61 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.type.Sequences +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionDeleteUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val crypto: KeyManagementRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: SignParams.DeleteParams) = supervisorScope { + logger.log("Session delete received on topic: ${request.topic}") + val irnParams = IrnParams(Tags.SESSION_DELETE_RESPONSE, Ttl(dayInSeconds)) + try { + if (!sessionStorageRepository.isSessionValid(request.topic)) { + logger.error("Session delete received failure on topic: ${request.topic} - invalid session") + jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) + return@supervisorScope + } + jsonRpcInteractor.unsubscribe(request.topic, + onSuccess = { + logger.log("Session delete received on topic: ${request.topic} - unsubscribe success") + try { + crypto.removeKeys(request.topic.value) + } catch (e: Exception) { + logger.error("Remove keys exception:$e") + } + }, + onFailure = { error -> logger.error("Session delete received on topic: ${request.topic} - unsubscribe error $error") }) + sessionStorageRepository.deleteSession(request.topic) + logger.log("Session delete received on topic: ${request.topic} - emitting") + _events.emit(params.toEngineDO(request.topic)) + } catch (e: Exception) { + logger.error("Session delete received failure on topic: ${request.topic} - $e") + jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot delete a session: ${e.message}, topic: ${request.topic}"), irnParams) + _events.emit(SDKError(e)) + return@supervisorScope + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionEventUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionEventUseCase.kt new file mode 100644 index 000000000..252eaaa1e --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionEventUseCase.kt @@ -0,0 +1,79 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.PeerError +import com.reown.sign.common.model.type.Sequences +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.engine.model.mapper.toEngineDOEvent +import com.reown.sign.engine.model.mapper.toPeerError +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionEventUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: SignParams.EventParams) = supervisorScope { + logger.log("Session event received on topic: ${request.topic}") + val irnParams = IrnParams(Tags.SESSION_EVENT_RESPONSE, Ttl(fiveMinutesInSeconds)) + try { + SignValidator.validateEvent(params.toEngineDOEvent()) { error -> + logger.error("Session event received failure on topic: ${request.topic} - $error") + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + if (!sessionStorageRepository.isSessionValid(request.topic)) { + logger.error("Session event received failure on topic: ${request.topic} - invalid session") + jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) + return@supervisorScope + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) + if (!session.isPeerController) { + logger.error("Session event received failure on topic: ${request.topic} - unauthorized peer") + jsonRpcInteractor.respondWithError(request, PeerError.Unauthorized.Event(Sequences.SESSION.name), irnParams) + return@supervisorScope + } + if (!session.isAcknowledged) { + logger.error("Session event received failure on topic: ${request.topic} - no matching topic") + jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) + return@supervisorScope + } + + val event = params.event + SignValidator.validateChainIdWithEventAuthorisation(params.chainId, event.name, session.sessionNamespaces) { error -> + logger.error("Session event received failure on topic: ${request.topic} - $error") + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + jsonRpcInteractor.respondWithSuccess(request, irnParams) + logger.log("Session event received on topic: ${request.topic} - emitting") + _events.emit(params.toEngineDO(request.topic)) + } catch (e: Exception) { + logger.error("Session event received failure on topic: ${request.topic} - $e") + jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot emit an event: ${e.message}, topic: ${request.topic}"), irnParams) + _events.emit(SDKError(e)) + return@supervisorScope + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionExtendUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionExtendUseCase.kt new file mode 100644 index 000000000..d4494c0cd --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionExtendUseCase.kt @@ -0,0 +1,62 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.type.Sequences +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.mapper.toEngineDOSessionExtend +import com.reown.sign.engine.model.mapper.toPeerError +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionExtendUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, requestParams: SignParams.ExtendParams) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_EXTEND_RESPONSE, Ttl(dayInSeconds)) + logger.log("Session extend received on topic: ${request.topic}") + try { + if (!sessionStorageRepository.isSessionValid(request.topic)) { + logger.error("Session extend received failure on topic: ${request.topic}") + jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) + return@supervisorScope + } + + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) + val newExpiry = requestParams.expiry + SignValidator.validateSessionExtend(newExpiry, session.expiry.seconds) { error -> + logger.error("Session extend received failure on topic: ${request.topic} - invalid request: $error") + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + sessionStorageRepository.extendSession(request.topic, newExpiry) + jsonRpcInteractor.respondWithSuccess(request, irnParams) + logger.log("Session extend received on topic: ${request.topic} - emitting") + _events.emit(session.toEngineDOSessionExtend(Expiry(newExpiry))) + } catch (e: Exception) { + logger.error("Session extend received failure on topic: ${request.topic}: $e") + jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot update a session: ${e.message}, topic: ${request.topic}"), irnParams) + _events.emit(SDKError(e)) + return@supervisorScope + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionProposalUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionProposalUseCase.kt new file mode 100644 index 000000000..24984adfa --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionProposalUseCase.kt @@ -0,0 +1,105 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.Core +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.verify.domain.ResolveAttestationIdUseCase +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.engine.model.mapper.toPeerError +import com.reown.sign.engine.model.mapper.toVO +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.storage.proposal.ProposalStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import org.koin.core.qualifier.named + +internal class OnSessionProposalUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val proposalStorageRepository: ProposalStorageRepository, + private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, + private val pairingController: PairingControllerInterface, + private val insertEventUseCase: InsertTelemetryEventUseCase, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + private val isAuthenticateEnabled: Boolean by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.ENABLE_AUTHENTICATE)) } + + suspend operator fun invoke(request: WCRequest, payloadParams: SignParams.SessionProposeParams) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_PROPOSE_RESPONSE_AUTO_REJECT, Ttl(fiveMinutesInSeconds)) + try { + if (isSessionAuthenticateImplemented(request)) { + logger.error("Session proposal received error: pairing supports authenticated sessions") + return@supervisorScope + } + logger.log("Session proposal received: ${request.topic}") + SignValidator.validateProposalNamespaces(payloadParams.requiredNamespaces) { error -> + logger.error("Session proposal received error: required namespace validation: ${error.message}") + insertEventUseCase(Props(type = EventType.Error.REQUIRED_NAMESPACE_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + SignValidator.validateProposalNamespaces(payloadParams.optionalNamespaces ?: emptyMap()) { error -> + logger.error("Session proposal received error: optional namespace validation: ${error.message}") + insertEventUseCase(Props(type = EventType.Error.OPTIONAL_NAMESPACE_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + payloadParams.properties?.let { + SignValidator.validateProperties(payloadParams.properties) { error -> + logger.error("Session proposal received error: session properties validation: ${error.message}") + insertEventUseCase(Props(type = EventType.Error.SESSION_PROPERTIES_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + } + proposalStorageRepository.insertProposal(payloadParams.toVO(request.topic, request.id)) + pairingController.setRequestReceived(Core.Params.RequestReceived(request.topic.value)) + val url = payloadParams.proposer.metadata.url + + logger.log("Resolving session proposal attestation: ${System.currentTimeMillis()}") + resolveAttestationIdUseCase(request, url, linkMode = request.transportType == TransportType.LINK_MODE, appLink = payloadParams.proposer.metadata.redirect?.universal) { verifyContext -> + logger.log("Session proposal attestation resolved: ${System.currentTimeMillis()}") + val sessionProposalEvent = EngineDO.SessionProposalEvent(proposal = payloadParams.toEngineDO(request.topic), context = verifyContext.toEngineDO()) + logger.log("Session proposal received on topic: ${request.topic} - emitting") + scope.launch { _events.emit(sessionProposalEvent) } + } + } catch (e: Exception) { + logger.error("Session proposal received error: $e") + jsonRpcInteractor.respondWithError( + request, + Uncategorized.GenericError("Cannot handle a session proposal: ${e.message}, topic: ${request.topic}"), + irnParams + ) + _events.emit(SDKError(e)) + } + } + + private fun isSessionAuthenticateImplemented(request: WCRequest): Boolean = + pairingController.getPairingByTopic(request.topic)?.methods?.contains(JsonRpcMethod.WC_SESSION_AUTHENTICATE) == true && isAuthenticateEnabled +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionRequestUseCase.kt new file mode 100644 index 000000000..58995bbd8 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionRequestUseCase.kt @@ -0,0 +1,142 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.exception.Invalid +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.CoreValidator.isExpired +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.verify.domain.ResolveAttestationIdUseCase +import com.reown.android.verify.model.VerifyContext +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.type.Sequences +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.engine.model.mapper.toPeerError +import com.reown.sign.engine.sessionRequestEventsQueue +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.Empty +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class OnSessionRequestUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, + private val insertEventUseCase: InsertEventUseCase, + private val clientId: String, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: SignParams.SessionRequestParams) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_REQUEST_RESPONSE, Ttl(fiveMinutesInSeconds)) + logger.log("Session request received on topic: ${request.topic}") + + try { + params.request.expiryTimestamp?.let { + if (Expiry(it).isExpired()) { + logger.error("Session request received failure on topic: ${request.topic} - request expired") + jsonRpcInteractor.respondWithError(request, Invalid.RequestExpired, irnParams) + return@supervisorScope + } + } + + SignValidator.validateSessionRequest(params.toEngineDO(request.topic)) { error -> + logger.error("Session request received failure on topic: ${request.topic} - invalid request") + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + if (!sessionStorageRepository.isSessionValid(request.topic)) { + logger.error("Session request received failure on topic: ${request.topic} - invalid session") + jsonRpcInteractor.respondWithError( + request, + Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), + irnParams + ) + return@supervisorScope + } + val (sessionNamespaces: Map, sessionPeerAppMetaData: AppMetaData?) = + sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) + .run { + val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) + this.sessionNamespaces to peerAppMetaData + } + + val method = params.request.method + SignValidator.validateChainIdWithMethodAuthorisation(params.chainId, method, sessionNamespaces) { error -> + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + if (request.transportType == TransportType.LINK_MODE) { + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_REQUEST_LINK_MODE.id.toString(), + Properties(correlationId = request.id, clientId = clientId, direction = Direction.RECEIVED.state) + ) + ) + } + + val url = sessionPeerAppMetaData?.url ?: String.Empty + logger.log("Resolving session request attestation: ${System.currentTimeMillis()}") + resolveAttestationIdUseCase(request, url, linkMode = request.transportType == TransportType.LINK_MODE, appLink = sessionPeerAppMetaData?.redirect?.universal) { verifyContext -> + logger.log("Session request attestation resolved: ${System.currentTimeMillis()}") + emitSessionRequest(params, request, sessionPeerAppMetaData, verifyContext) + } + } catch (e: Exception) { + logger.error("Session request received failure on topic: ${request.topic} - ${e.message}") + jsonRpcInteractor.respondWithError( + request, + Uncategorized.GenericError("Cannot handle a session request: ${e.message}, topic: ${request.topic}"), + irnParams + ) + _events.emit(SDKError(e)) + return@supervisorScope + } + } + + private fun emitSessionRequest( + params: SignParams.SessionRequestParams, + request: WCRequest, + sessionPeerAppMetaData: AppMetaData?, + verifyContext: VerifyContext + ) { + val sessionRequestEvent = EngineDO.SessionRequestEvent(params.toEngineDO(request, sessionPeerAppMetaData), verifyContext.toEngineDO()) + val event = if (sessionRequestEventsQueue.isEmpty()) { + sessionRequestEvent + } else { + sessionRequestEventsQueue.find { event -> if (event.request.expiry != null) !event.request.expiry.isExpired() else true } ?: sessionRequestEvent + } + + sessionRequestEventsQueue.add(sessionRequestEvent) + logger.log("Session request received on topic: ${request.topic} - emitting") + scope.launch { _events.emit(event) } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionSettleUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionSettleUseCase.kt new file mode 100644 index 000000000..0c23cc7e2 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionSettleUseCase.kt @@ -0,0 +1,102 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.Core +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.utils.toClient +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.PeerError +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.mapper.toPeerError +import com.reown.sign.engine.model.mapper.toSessionApproved +import com.reown.sign.storage.proposal.ProposalStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.Empty +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionSettleUseCase( + private val crypto: KeyManagementRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val proposalStorageRepository: ProposalStorageRepository, + private val sessionStorageRepository: SessionStorageRepository, + private val pairingController: PairingControllerInterface, + private val selfAppMetaData: AppMetaData, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, settleParams: SignParams.SessionSettleParams) = supervisorScope { + logger.log("Session settle received on topic: ${request.topic}") + val sessionTopic = request.topic + val irnParams = IrnParams(Tags.SESSION_SETTLE_RESPONSE, Ttl(fiveMinutesInSeconds)) + val selfPublicKey: PublicKey = try { + crypto.getSelfPublicFromKeyAgreement(sessionTopic) + } catch (e: Exception) { + logger.error("Session settle received failure: ${request.topic}, error: $e") + jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) + return@supervisorScope + } + + val peerMetadata = settleParams.controller.metadata + val proposal = try { + proposalStorageRepository.getProposalByKey(selfPublicKey.keyAsHex).also { proposalStorageRepository.deleteProposal(selfPublicKey.keyAsHex) } + } catch (e: Exception) { + logger.error("Session settle received failure: ${request.topic}, error: $e") + jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) + return@supervisorScope + } + + val (requiredNamespaces, optionalNamespaces, properties) = proposal.run { Triple(requiredNamespaces, optionalNamespaces, properties) } + SignValidator.validateSessionNamespace(settleParams.namespaces, requiredNamespaces) { error -> + logger.error("Session settle received failure - namespace validation: ${request.topic}, error: $error") + jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) + return@supervisorScope + } + + try { + val session = SessionVO.createAcknowledgedSession( + sessionTopic, + settleParams, + selfPublicKey, + selfAppMetaData, + requiredNamespaces, + optionalNamespaces, + properties, + proposal.pairingTopic.value + ) + + sessionStorageRepository.insertSession(session, request.id) + pairingController.updateMetadata(Core.Params.UpdateMetadata(proposal.pairingTopic.value, peerMetadata.toClient(), AppMetaDataType.PEER)) + metadataStorageRepository.insertOrAbortMetadata(sessionTopic, peerMetadata, AppMetaDataType.PEER) + jsonRpcInteractor.respondWithSuccess(request, irnParams) + logger.log("Session settle received on topic: ${request.topic} - emitting") + _events.emit(session.toSessionApproved()) + } catch (e: Exception) { + logger.error("Session settle received failure: ${request.topic}, error: $e") + proposalStorageRepository.insertProposal(proposal) + sessionStorageRepository.deleteSession(sessionTopic) + jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) + _events.emit(SDKError(e)) + return@supervisorScope + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt new file mode 100644 index 000000000..c109b98f0 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt @@ -0,0 +1,79 @@ +package com.reown.sign.engine.use_case.requests + +import com.reown.android.internal.common.exception.Uncategorized +import com.reown.android.internal.common.model.IrnParams +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.WCRequest +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.dayInSeconds +import com.reown.foundation.common.model.Ttl +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.PeerError +import com.reown.sign.common.model.type.Sequences +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toMapOfEngineNamespacesSession +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.extractTimestamp +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionUpdateUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val sessionStorageRepository: SessionStorageRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(request: WCRequest, params: SignParams.UpdateNamespacesParams) = supervisorScope { + val irnParams = IrnParams(Tags.SESSION_UPDATE_RESPONSE, Ttl(dayInSeconds)) + logger.log("Session update received on topic: ${request.topic}") + try { + if (!sessionStorageRepository.isSessionValid(request.topic)) { + logger.error("Session update received failure on topic: ${request.topic} - invalid session") + jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) + return@supervisorScope + } + + val session: SessionVO = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) + if (!session.isPeerController) { + logger.error("Session update received failure on topic: ${request.topic} - unauthorized peer") + jsonRpcInteractor.respondWithError(request, PeerError.Unauthorized.UpdateRequest(Sequences.SESSION.name), irnParams) + return@supervisorScope + } + + SignValidator.validateSessionNamespace(params.namespaces, session.requiredNamespaces) { error -> + logger.error("Session update received failure on topic: ${request.topic} - namespaces validation: $error") + jsonRpcInteractor.respondWithError(request, PeerError.Invalid.UpdateRequest(error.message), irnParams) + return@supervisorScope + } + + if (!sessionStorageRepository.isUpdatedNamespaceValid(session.topic.value, request.id.extractTimestamp())) { + logger.error("Session update received failure on topic: ${request.topic} - Update Namespace Request ID too old") + jsonRpcInteractor.respondWithError(request, PeerError.Invalid.UpdateRequest("Update Namespace Request ID too old"), irnParams) + return@supervisorScope + } + + sessionStorageRepository.deleteNamespaceAndInsertNewNamespace(session.topic.value, params.namespaces, request.id) + jsonRpcInteractor.respondWithSuccess(request, irnParams) + logger.log("Session update received on topic: ${request.topic} - emitting") + _events.emit(EngineDO.SessionUpdateNamespaces(request.topic, params.namespaces.toMapOfEngineNamespacesSession())) + } catch (e: Exception) { + logger.error("Session update received failure on topic: ${request.topic} - $e") + jsonRpcInteractor.respondWithError( + request, + PeerError.Invalid.UpdateRequest("Updating Namespace Failed. Review Namespace structure. Error: ${e.message}, topic: ${request.topic}"), + irnParams + ) + _events.emit(SDKError(e)) + return@supervisorScope + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt similarity index 77% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt index 8bc06f516..55ddf50c6 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionAuthenticateResponseUseCase.kt @@ -1,44 +1,44 @@ -package com.walletconnect.sign.engine.use_case.responses +package com.reown.sign.engine.use_case.responses -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.signing.cacao.getChains -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.utils.toClient -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.json_rpc.domain.GetSessionAuthenticateRequest -import com.walletconnect.sign.storage.authenticate.AuthenticateResponseTopicRepository -import com.walletconnect.sign.storage.link_mode.LinkModeStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.Core +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.signing.cacao.Issuer +import com.reown.android.internal.common.signing.cacao.getChains +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.utils.CoreValidator +import com.reown.android.pairing.client.PairingInterface +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.android.utils.toClient +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.json_rpc.domain.GetSessionAuthenticateRequest +import com.reown.sign.storage.authenticate.AuthenticateResponseTopicRepository +import com.reown.sign.storage.link_mode.LinkModeStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt new file mode 100644 index 000000000..fa707ab69 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt @@ -0,0 +1,67 @@ +package com.reown.sign.engine.use_case.responses + +import com.reown.android.Core +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.params.CoreSignParams +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.scope +import com.reown.android.pairing.handler.PairingControllerInterface +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.proposal.ProposalStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope + +internal class OnSessionProposalResponseUseCase( + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val pairingController: PairingControllerInterface, + private val crypto: KeyManagementRepository, + private val proposalStorageRepository: ProposalStorageRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse, params: SignParams.SessionProposeParams) = supervisorScope { + try { + logger.log("Session proposal response received on topic: ${wcResponse.topic}") + val pairingTopic = wcResponse.topic + pairingController.deleteAndUnsubscribePairing(Core.Params.Delete(pairingTopic.value)) + when (val response = wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + logger.log("Session proposal approval received on topic: ${wcResponse.topic}") + val selfPublicKey = PublicKey(params.proposer.publicKey) + val approveParams = response.result as CoreSignParams.ApprovalParams + val responderPublicKey = PublicKey(approveParams.responderPublicKey) + val sessionTopic = crypto.generateTopicFromKeyAgreement(selfPublicKey, responderPublicKey) + + jsonRpcInteractor.subscribe(sessionTopic, + onSuccess = { logger.log("Session proposal approval subscribed on session topic: $sessionTopic") }, + onFailure = { error -> + logger.error("Session proposal approval subscribe error on session topic: $sessionTopic - $error") + scope.launch { _events.emit(SDKError(error)) } + } + ) + } + + is JsonRpcResponse.JsonRpcError -> { + proposalStorageRepository.deleteProposal(params.proposer.publicKey) + logger.log("Session proposal rejection received on topic: ${wcResponse.topic}") + _events.emit(EngineDO.SessionRejected(pairingTopic.value, response.errorMessage)) + } + } + } catch (e: Exception) { + logger.error("Session proposal response received failure on topic: ${wcResponse.topic}: $e") + _events.emit(SDKError(e)) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt new file mode 100644 index 000000000..54b00bc52 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt @@ -0,0 +1,60 @@ +package com.reown.sign.engine.use_case.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.Tags +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.model.Direction +import com.reown.android.pulse.model.EventType +import com.reown.android.pulse.model.properties.Properties +import com.reown.android.pulse.model.properties.Props +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.json_rpc.domain.GetSessionRequestByIdUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionRequestResponseUseCase( + private val logger: Logger, + private val insertEventUseCase: InsertEventUseCase, + private val getSessionRequestByIdUseCase: GetSessionRequestByIdUseCase, + private val clientId: String +) { + + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse, params: SignParams.SessionRequestParams) = supervisorScope { + try { + val jsonRpcHistoryEntry = getSessionRequestByIdUseCase(wcResponse.response.id) + logger.log("Session request response received on topic: ${wcResponse.topic}") + val result = when (wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> (wcResponse.response as JsonRpcResponse.JsonRpcResult).toEngineDO() + is JsonRpcResponse.JsonRpcError -> (wcResponse.response as JsonRpcResponse.JsonRpcError).toEngineDO() + } + + if (jsonRpcHistoryEntry?.transportType == TransportType.LINK_MODE) { + insertEventUseCase( + Props( + EventType.SUCCESS, + Tags.SESSION_REQUEST_LINK_MODE_RESPONSE.id.toString(), + Properties(correlationId = wcResponse.response.id, clientId = clientId, direction = Direction.RECEIVED.state) + ) + ) + } + val method = params.request.method + logger.log("Session request response received on topic: ${wcResponse.topic} - emitting: $result") + _events.emit(EngineDO.SessionPayloadResponse(wcResponse.topic.value, params.chainId, method, result)) + } catch (e: Exception) { + logger.error("Session request response received failure: $e") + _events.emit(SDKError(e)) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt new file mode 100644 index 000000000..9b46b931b --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt @@ -0,0 +1,65 @@ +package com.reown.sign.engine.use_case.responses + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.foundation.util.Logger +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toEngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.supervisorScope + +internal class OnSessionSettleResponseUseCase( + private val sessionStorageRepository: SessionStorageRepository, + private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, + private val crypto: KeyManagementRepository, + private val logger: Logger +) { + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow = _events.asSharedFlow() + + suspend operator fun invoke(wcResponse: WCResponse) = supervisorScope { + try { + logger.log("Session settle response received on topic: ${wcResponse.topic}") + val sessionTopic = wcResponse.topic + if (!sessionStorageRepository.isSessionValid(sessionTopic)) { + logger.error("Peer failed to settle session: invalid session") + return@supervisorScope + } + val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(sessionTopic).run { + val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) + this.copy(selfAppMetaData = selfAppMetaData, peerAppMetaData = peerAppMetaData) + } + + when (wcResponse.response) { + is JsonRpcResponse.JsonRpcResult -> { + logger.log("Session settle success received") + sessionStorageRepository.acknowledgeSession(sessionTopic) + _events.emit(EngineDO.SettledSessionResponse.Result(session.toEngineDO())) + } + + is JsonRpcResponse.JsonRpcError -> { + logger.error("Peer failed to settle session: ${(wcResponse.response as JsonRpcResponse.JsonRpcError).errorMessage}") + jsonRpcInteractor.unsubscribe(sessionTopic, onSuccess = { + runCatching { + sessionStorageRepository.deleteSession(sessionTopic) + crypto.removeKeys(sessionTopic.value) + }.onFailure { logger.error(it) } + }) + } + } + } catch (e: Exception) { + logger.error("Peer failed to settle session: $e") + _events.emit(SDKError(e)) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt similarity index 79% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt index 72633a749..e0ca10066 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/engine/use_case/responses/OnSessionUpdateResponseUseCase.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sign.engine.use_case.responses +package com.reown.sign.engine.use_case.responses -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toMapOfEngineNamespacesSession -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.extractTimestamp +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.model.SDKError +import com.reown.android.internal.common.model.WCResponse +import com.reown.android.internal.common.model.type.EngineEvent +import com.reown.foundation.util.Logger +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toMapOfEngineNamespacesSession +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.utils.extractTimestamp import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt new file mode 100644 index 000000000..f237e6d58 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt @@ -0,0 +1,18 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import kotlinx.coroutines.supervisorScope + +internal class DeleteRequestByIdUseCase( + private val jsonRpcHistory: JsonRpcHistory, + private val verifyContextStorageRepository: VerifyContextStorageRepository +) { + + suspend operator fun invoke(id: Long) { + supervisorScope { + jsonRpcHistory.deleteRecordById(id) + verifyContextStorageRepository.delete(id) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt new file mode 100644 index 000000000..476cee6c2 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt @@ -0,0 +1,29 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.toRequest + +internal class GetPendingJsonRpcHistoryEntryByIdUseCase( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) { + + operator fun invoke(id: Long): Request? { + val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) + var entry: Request? = null + + if (record != null) { + val sessionRequest: SignRpc.SessionRequest? = serializer.tryDeserialize(record.body) + if (sessionRequest != null) { + entry = record.toRequest(sessionRequest.params) + } + } + + return entry + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt new file mode 100644 index 000000000..ab0aac1ce --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt @@ -0,0 +1,26 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.json_rpc.model.toRequest +import kotlinx.coroutines.supervisorScope + +internal class GetPendingRequestsUseCaseByTopic( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) : GetPendingRequestsUseCaseByTopicInterface { + + override suspend fun getPendingRequests(topic: Topic): List> = supervisorScope { + jsonRpcHistory.getListOfPendingRecordsByTopic(topic) + .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } + .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } + } +} + +internal interface GetPendingRequestsUseCaseByTopicInterface { + suspend fun getPendingRequests(topic: Topic): List> +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt new file mode 100644 index 000000000..a8d66157d --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt @@ -0,0 +1,29 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.toRequest + +class GetPendingSessionAuthenticateRequest( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) { + + internal operator fun invoke(id: Long): Request? { + val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) + var entry: Request? = null + + if (record != null) { + val authRequest: SignRpc.SessionAuthenticate? = serializer.tryDeserialize(record.body) + if (authRequest != null) { + entry = record.toRequest(authRequest.params) + } + } + + return entry + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt new file mode 100644 index 000000000..4031c048d --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt @@ -0,0 +1,33 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.model.AppMetaDataType +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toSessionRequest +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.json_rpc.model.toRequest +import kotlinx.coroutines.supervisorScope + +internal class GetPendingSessionRequestByTopicUseCase( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer, + private val metadataStorageRepository: MetadataStorageRepositoryInterface, +) : GetPendingSessionRequestByTopicUseCaseInterface { + + override suspend fun getPendingSessionRequests(topic: Topic): List = supervisorScope { + jsonRpcHistory.getListOfPendingRecordsByTopic(topic) + .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } + .mapNotNull { record -> + serializer.tryDeserialize(record.body)?.toRequest(record) + ?.toSessionRequest(metadataStorageRepository.getByTopicAndType(Topic(record.topic), AppMetaDataType.PEER)) + } + } +} + +internal interface GetPendingSessionRequestByTopicUseCaseInterface { + suspend fun getPendingSessionRequests(topic: Topic): List +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequests.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequests.kt new file mode 100644 index 000000000..7077c148b --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetPendingSessionRequests.kt @@ -0,0 +1,21 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.json_rpc.model.JsonRpcMethod +import com.reown.sign.json_rpc.model.toRequest +import kotlinx.coroutines.supervisorScope + +internal class GetPendingSessionRequests( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) { + + suspend operator fun invoke(): List> = supervisorScope { + jsonRpcHistory.getListOfPendingRecords() + .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } + .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt new file mode 100644 index 000000000..58b14a687 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt @@ -0,0 +1,28 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.toRequest + +internal class GetSessionAuthenticateRequest( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) { + internal operator fun invoke(id: Long): Request? { + val record: JsonRpcHistoryRecord? = jsonRpcHistory.getRecordById(id) + var entry: Request? = null + + if (record != null) { + val authRequest: SignRpc.SessionAuthenticate? = serializer.tryDeserialize(record.body) + if (authRequest != null) { + entry = record.toRequest(authRequest.params) + } + } + + return entry + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt new file mode 100644 index 000000000..6091d0a3a --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt @@ -0,0 +1,28 @@ +package com.reown.sign.json_rpc.domain + +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.model.toRequest + +internal class GetSessionRequestByIdUseCase( + private val jsonRpcHistory: JsonRpcHistory, + private val serializer: JsonRpcSerializer +) { + operator fun invoke(id: Long): Request? { + val record: JsonRpcHistoryRecord? = jsonRpcHistory.getRecordById(id) + var entry: Request? = null + + if (record != null) { + val sessionRequest: SignRpc.SessionRequest? = serializer.tryDeserialize(record.body) + if (sessionRequest != null) { + entry = record.toRequest(sessionRequest.params) + } + } + + return entry + } +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMapper.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMapper.kt new file mode 100644 index 000000000..f572436c3 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMapper.kt @@ -0,0 +1,57 @@ +@file:JvmSynthetic + +package com.reown.sign.json_rpc.model + +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.model.Expiry +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams + +@JvmSynthetic +internal fun SignRpc.SessionRequest.toRequest(entry: JsonRpcHistoryRecord): Request = + Request( + entry.id, + Topic(entry.topic), + params.request.method, + params.chainId, + params.request.params, + if (params.request.expiryTimestamp != null) Expiry(params.request.expiryTimestamp) else null, + transportType = entry.transportType + ) + +@JvmSynthetic +internal fun JsonRpcHistoryRecord.toRequest(params: SignParams.SessionRequestParams): Request = + Request( + id, + Topic(topic), + method, + params.chainId, + params, + transportType = transportType + ) + +@JvmSynthetic +internal fun JsonRpcHistoryRecord.toRequest(params: SignParams.SessionAuthenticateParams): Request = + Request( + id, + Topic(topic), + method, + null, + params, + Expiry(params.expiryTimestamp), + transportType = transportType + ) + +@JvmSynthetic +internal fun SignRpc.SessionAuthenticate.toRequest(entry: JsonRpcHistoryRecord): Request = + Request( + entry.id, + Topic(entry.topic), + entry.method, + null, + params, + Expiry(params.expiryTimestamp), + transportType = entry.transportType + ) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMethod.kt b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMethod.kt new file mode 100644 index 000000000..4a4133fa6 --- /dev/null +++ b/protocol/sign/src/main/kotlin/com/reown/sign/json_rpc/model/JsonRpcMethod.kt @@ -0,0 +1,24 @@ +@file:JvmSynthetic + +package com.reown.sign.json_rpc.model + +internal object JsonRpcMethod { + @get:JvmSynthetic + const val WC_SESSION_PROPOSE: String = "wc_sessionPropose" + @get:JvmSynthetic + const val WC_SESSION_AUTHENTICATE: String = "wc_sessionAuthenticate" + @get:JvmSynthetic + const val WC_SESSION_SETTLE: String = "wc_sessionSettle" + @get:JvmSynthetic + const val WC_SESSION_REQUEST: String = "wc_sessionRequest" + @get:JvmSynthetic + const val WC_SESSION_DELETE: String = "wc_sessionDelete" + @get:JvmSynthetic + const val WC_SESSION_PING: String = "wc_sessionPing" + @get:JvmSynthetic + const val WC_SESSION_EVENT: String = "wc_sessionEvent" + @get:JvmSynthetic + const val WC_SESSION_UPDATE: String = "wc_sessionUpdate" + @get:JvmSynthetic + const val WC_SESSION_EXTEND: String = "wc_sessionExtend" +} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt b/protocol/sign/src/main/kotlin/com/reown/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt similarity index 84% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt index 10e06fddc..3692ba9bd 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/storage/authenticate/AuthenticateResponseTopicRepository.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sign.storage.authenticate +package com.reown.sign.storage.authenticate import android.database.sqlite.SQLiteException -import com.walletconnect.sign.storage.data.dao.authenticatereponse.AuthenticateResponseTopicDaoQueries +import com.reown.sign.storage.data.dao.authenticatereponse.AuthenticateResponseTopicDaoQueries internal class AuthenticateResponseTopicRepository(private val authenticateResponseTopicDaoQueries: AuthenticateResponseTopicDaoQueries) { @JvmSynthetic diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/link_mode/LinkModeStorageRepository.kt b/protocol/sign/src/main/kotlin/com/reown/sign/storage/link_mode/LinkModeStorageRepository.kt similarity index 83% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/link_mode/LinkModeStorageRepository.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/storage/link_mode/LinkModeStorageRepository.kt index c77052a6a..2f27eb755 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/link_mode/LinkModeStorageRepository.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/storage/link_mode/LinkModeStorageRepository.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sign.storage.link_mode +package com.reown.sign.storage.link_mode import android.database.sqlite.SQLiteException -import com.walletconnect.sign.storage.data.dao.linkmode.LinkModeDaoQueries +import com.reown.sign.storage.data.dao.linkmode.LinkModeDaoQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/proposal/ProposalStorageRepository.kt b/protocol/sign/src/main/kotlin/com/reown/sign/storage/proposal/ProposalStorageRepository.kt similarity index 88% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/proposal/ProposalStorageRepository.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/storage/proposal/ProposalStorageRepository.kt index 326b8435d..7b3850457 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/proposal/ProposalStorageRepository.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/storage/proposal/ProposalStorageRepository.kt @@ -1,17 +1,17 @@ @file:JvmSynthetic -package com.walletconnect.sign.storage.proposal +package com.reown.sign.storage.proposal import android.database.sqlite.SQLiteException import app.cash.sqldelight.async.coroutines.awaitAsList -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.scope -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDaoQueries -import com.walletconnect.sign.storage.data.dao.proposal.ProposalDaoQueries -import com.walletconnect.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDaoQueries +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.scope +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDaoQueries +import com.reown.sign.storage.data.dao.proposal.ProposalDaoQueries +import com.reown.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDaoQueries import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/sequence/SessionStorageRepository.kt b/protocol/sign/src/main/kotlin/com/reown/sign/storage/sequence/SessionStorageRepository.kt similarity index 91% rename from protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/sequence/SessionStorageRepository.kt rename to protocol/sign/src/main/kotlin/com/reown/sign/storage/sequence/SessionStorageRepository.kt index 13217fca4..cff86c4b9 100644 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/storage/sequence/SessionStorageRepository.kt +++ b/protocol/sign/src/main/kotlin/com/reown/sign/storage/sequence/SessionStorageRepository.kt @@ -1,22 +1,22 @@ @file:JvmSynthetic -package com.walletconnect.sign.storage.sequence +package com.reown.sign.storage.sequence import android.database.sqlite.SQLiteException -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.engine.sessionRequestEventsQueue -import com.walletconnect.sign.storage.data.dao.namespace.NamespaceDaoQueries -import com.walletconnect.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDaoQueries -import com.walletconnect.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDaoQueries -import com.walletconnect.sign.storage.data.dao.session.SessionDaoQueries -import com.walletconnect.sign.storage.data.dao.temp.TempNamespaceDaoQueries -import com.walletconnect.utils.Empty -import com.walletconnect.utils.isSequenceValid +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.TransportType +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.engine.sessionRequestEventsQueue +import com.reown.sign.storage.data.dao.namespace.NamespaceDaoQueries +import com.reown.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDaoQueries +import com.reown.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDaoQueries +import com.reown.sign.storage.data.dao.session.SessionDaoQueries +import com.reown.sign.storage.data.dao.temp.TempNamespaceDaoQueries +import com.reown.utils.Empty +import com.reown.utils.isSequenceValid internal class SessionStorageRepository( private val sessionDaoQueries: SessionDaoQueries, diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignInterface.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignInterface.kt deleted file mode 100644 index 0be223d60..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/SignInterface.kt +++ /dev/null @@ -1,187 +0,0 @@ -package com.walletconnect.sign.client - -interface SignInterface { - interface WalletDelegate { - fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal, verifyContext: Sign.Model.VerifyContext) - val onSessionAuthenticate: ((Sign.Model.SessionAuthenticate, Sign.Model.VerifyContext) -> Unit)? get() = null - fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest, verifyContext: Sign.Model.VerifyContext) - fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) - fun onSessionExtend(session: Sign.Model.Session) - - //Responses - fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) - fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) - - //Utils - fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) { - //override me - } - - fun onRequestExpired(request: Sign.Model.ExpiredRequest) { - //override me - } - - fun onConnectionStateChange(state: Sign.Model.ConnectionState) - fun onError(error: Sign.Model.Error) - } - - interface DappDelegate { - fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) - fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) - fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) - - @Deprecated( - - message = "onSessionEvent is deprecated. Use onEvent instead. Using both will result in duplicate events.", - replaceWith = ReplaceWith(expression = "onEvent(event)") - ) - fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) - fun onSessionEvent(sessionEvent: Sign.Model.Event) {} - fun onSessionExtend(session: Sign.Model.Session) - fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) - - //Responses - fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) - fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {} - - // Utils - fun onProposalExpired(proposal: Sign.Model.ExpiredProposal) - fun onRequestExpired(request: Sign.Model.ExpiredRequest) - fun onConnectionStateChange(state: Sign.Model.ConnectionState) - fun onError(error: Sign.Model.Error) - } - - fun initialize(init: Sign.Params.Init, onSuccess: () -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun setWalletDelegate(delegate: WalletDelegate) - fun setDappDelegate(delegate: DappDelegate) - - @Deprecated( - message = "Replaced with the same name method but onSuccess callback returns a Pairing URL", - replaceWith = ReplaceWith(expression = "fun connect(connect: Sign.Params.Connect, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit)") - ) - fun connect( - connect: Sign.Params.Connect, onSuccess: () -> Unit, - onError: (Sign.Model.Error) -> Unit, - ) - - fun connect( - connect: Sign.Params.Connect, onSuccess: (String) -> Unit, - onError: (Sign.Model.Error) -> Unit, - ) - - fun authenticate(authenticate: Sign.Params.Authenticate, walletAppLink: String? = null, onSuccess: (String) -> Unit, onError: (Sign.Model.Error) -> Unit) - fun dispatchEnvelope(urlWithEnvelope: String, onError: (Sign.Model.Error) -> Unit) - - @Deprecated( - message = "Creating a pairing will be moved to CoreClient to make pairing SDK agnostic", - replaceWith = ReplaceWith(expression = "CoreClient.Pairing.pair()", imports = ["com.walletconnect.android.CoreClient"]) - ) - fun pair(pair: Sign.Params.Pair, onSuccess: (Sign.Params.Pair) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun approveSession(approve: Sign.Params.Approve, onSuccess: (Sign.Params.Approve) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun rejectSession(reject: Sign.Params.Reject, onSuccess: (Sign.Params.Reject) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun approveAuthenticate(approve: Sign.Params.ApproveAuthenticate, onSuccess: (Sign.Params.ApproveAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) - fun rejectAuthenticate(reject: Sign.Params.RejectAuthenticate, onSuccess: (Sign.Params.RejectAuthenticate) -> Unit, onError: (Sign.Model.Error) -> Unit) - fun formatAuthMessage(formatMessage: Sign.Params.FormatMessage): String - - @Deprecated( - message = "The onSuccess callback has been replaced with a new callback that returns Sign.Model.SentRequest", - replaceWith = ReplaceWith(expression = "this.request(request, onSuccessWithSentRequest, onError)", imports = ["com.walletconnect.sign.client"]) - ) - fun request( - request: Sign.Params.Request, - onSuccess: (Sign.Params.Request) -> Unit = {}, - onSuccessWithSentRequest: (Sign.Model.SentRequest) -> Unit = { it: Sign.Model.SentRequest -> Unit }, - onError: (Sign.Model.Error) -> Unit, - ) - - fun request(request: Sign.Params.Request, onSuccess: (Sign.Model.SentRequest) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun respond(response: Sign.Params.Response, onSuccess: (Sign.Params.Response) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun update(update: Sign.Params.Update, onSuccess: (Sign.Params.Update) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun extend(extend: Sign.Params.Extend, onSuccess: (Sign.Params.Extend) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun emit(emit: Sign.Params.Emit, onSuccess: (Sign.Params.Emit) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun ping(ping: Sign.Params.Ping, sessionPing: Sign.Listeners.SessionPing? = null) - fun disconnect(disconnect: Sign.Params.Disconnect, onSuccess: (Sign.Params.Disconnect) -> Unit = {}, onError: (Sign.Model.Error) -> Unit) - fun decryptMessage(params: Sign.Params.DecryptMessage, onSuccess: (Sign.Model.Message) -> Unit, onError: (Sign.Model.Error) -> Unit) - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getListOfActiveSessions(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getActiveSessionByTopic(topic: String): Sign.Model.Session? - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - message = "Getting a list of settled sessions is replaced with getListOfActiveSessions()", - replaceWith = ReplaceWith(expression = "SignClient.getListOfActiveSessions()") - ) - fun getListOfSettledSessions(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - message = "Getting a list of settled sessions by topic is replaced with getSettledSessionByTopic()", - replaceWith = ReplaceWith(expression = "SignClient.getSettledSessionByTopic()") - ) - fun getSettledSessionByTopic(topic: String): Sign.Model.Session? - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - message = "Getting a list of Pairings will be moved to CoreClient to make pairing SDK agnostic", - replaceWith = ReplaceWith(expression = "CoreClient.Pairing.getPairings()") - ) - fun getListOfSettledPairings(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - @Deprecated( - "The return type of getPendingRequests methods has been replaced with SessionRequest list", - replaceWith = ReplaceWith("getPendingSessionRequests(topic: String): List") - ) - fun getPendingRequests(topic: String): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getPendingSessionRequests(topic: String): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getSessionProposals(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getVerifyContext(id: Long): Sign.Model.VerifyContext? - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getPendingAuthenticateRequests(): List - - /** - * Caution: This function is blocking and runs on the current thread. - * It is advised that this function be called from background operation - */ - fun getListOfVerifyContexts(): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/mapper/ClientMapper.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/mapper/ClientMapper.kt deleted file mode 100644 index 824bf758c..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/mapper/ClientMapper.kt +++ /dev/null @@ -1,463 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.client.mapper - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.ConnectionState -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.RECAPS_STATEMENT -import com.walletconnect.android.internal.common.signing.cacao.getStatement -import com.walletconnect.android.utils.toClient -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.EngineDO - -@JvmSynthetic -internal fun Sign.Model.JsonRpcResponse.toJsonRpcResponse(): JsonRpcResponse = - when (this) { - is Sign.Model.JsonRpcResponse.JsonRpcResult -> this.toRpcResult() - is Sign.Model.JsonRpcResponse.JsonRpcError -> this.toRpcError() - } - -@JvmSynthetic -internal fun EngineDO.SettledSessionResponse.toClientSettledSessionResponse(): Sign.Model.SettledSessionResponse = - when (this) { - is EngineDO.SettledSessionResponse.Result -> Sign.Model.SettledSessionResponse.Result(settledSession.toClientActiveSession()) - is EngineDO.SettledSessionResponse.Error -> Sign.Model.SettledSessionResponse.Error(errorMessage) - } - -@JvmSynthetic -internal fun EngineDO.SessionAuthenticateResponse.toClientSessionAuthenticateResponse(): Sign.Model.SessionAuthenticateResponse = - when (this) { - is EngineDO.SessionAuthenticateResponse.Result -> Sign.Model.SessionAuthenticateResponse.Result(id, cacaos.toClient(), session?.toClientActiveSession()) - is EngineDO.SessionAuthenticateResponse.Error -> Sign.Model.SessionAuthenticateResponse.Error(id, code, message) - } - -@JvmSynthetic -internal fun EngineDO.SessionUpdateNamespacesResponse.toClientUpdateSessionNamespacesResponse(): Sign.Model.SessionUpdateResponse = - when (this) { - is EngineDO.SessionUpdateNamespacesResponse.Result -> Sign.Model.SessionUpdateResponse.Result(topic.value, namespaces.toMapOfClientNamespacesSession()) - is EngineDO.SessionUpdateNamespacesResponse.Error -> Sign.Model.SessionUpdateResponse.Error(errorMessage) - } - -@JvmSynthetic -internal fun EngineDO.JsonRpcResponse.toClientJsonRpcResponse(): Sign.Model.JsonRpcResponse = - when (this) { - is EngineDO.JsonRpcResponse.JsonRpcResult -> this.toClientJsonRpcResult() - is EngineDO.JsonRpcResponse.JsonRpcError -> this.toClientJsonRpcError() - } - -@JvmSynthetic -internal fun EngineDO.SessionProposal.toClientSessionProposal(): Sign.Model.SessionProposal = - Sign.Model.SessionProposal( - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toMapOfClientNamespacesProposal(), - optionalNamespaces.toMapOfClientNamespacesProposal(), - properties, - proposerPublicKey, - relayProtocol, - relayData - ) - -@JvmSynthetic -internal fun EngineDO.VerifyContext.toCore(): Sign.Model.VerifyContext = - Sign.Model.VerifyContext(id, origin, this.validation.toClientValidation(), verifyUrl, isScam) - -internal fun Validation.toClientValidation(): Sign.Model.Validation = - when (this) { - Validation.VALID -> Sign.Model.Validation.VALID - Validation.INVALID -> Sign.Model.Validation.INVALID - Validation.UNKNOWN -> Sign.Model.Validation.UNKNOWN - } - -@JvmSynthetic -internal fun EngineDO.SessionRequest.toClientSessionRequest(): Sign.Model.SessionRequest = - Sign.Model.SessionRequest( - topic = topic, - chainId = chainId, - peerMetaData = peerAppMetaData?.toClient(), - request = Sign.Model.SessionRequest.JSONRPCRequest( - id = request.id, - method = request.method, - params = request.params - ) - ) - -@JvmSynthetic -internal fun Sign.Model.PayloadParams.toEngine(): EngineDO.PayloadParams = with(this) { - EngineDO.PayloadParams( - type = CacaoType.CAIP222.header, - chains = chains, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat, - version = "1" - ) -} - -@JvmSynthetic -internal fun Sign.Params.Authenticate.toAuthenticate(): EngineDO.Authenticate = with(this) { - EngineDO.Authenticate( - type = CacaoType.EIP4361.header, - chains = chains, - domain = domain, - aud = uri, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - methods = methods, - expiry = expiry - ) -} - -@JvmSynthetic -internal fun Sign.Model.PayloadParams.toCacaoPayload(issuer: String): Sign.Model.Cacao.Payload = with(this) { - Sign.Model.Cacao.Payload(issuer, domain, aud, "1", nonce, iat = iat, nbf, exp, getStatement(), requestId, resources) -} - -private fun Sign.Model.PayloadParams.getStatement() = - if (statement?.contains(RECAPS_STATEMENT) == true) { - statement - } else { - Pair(statement, resources).getStatement() - } - -@JvmSynthetic -internal fun List.toCommon(): List = this.map { - with(it) { - Cacao( - Cacao.Header(header.t), - Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Cacao.Signature(signature.t, signature.s, signature.m) - ) - } -} - -@JvmSynthetic -internal fun List.toClient(): List = this.map { - with(it) { - Sign.Model.Cacao( - Sign.Model.Cacao.Header(header.t), - Sign.Model.Cacao.Payload( - payload.iss, - payload.domain, - payload.aud, - payload.version, - payload.nonce, - payload.iat, - payload.nbf, - payload.exp, - payload.statement, - payload.requestId, - payload.resources - ), - Sign.Model.Cacao.Signature(signature.t, signature.s, signature.m) - ) - } -} - -@JvmSynthetic -internal fun Sign.Model.JsonRpcResponse.JsonRpcResult.toRpcResult(): JsonRpcResponse.JsonRpcResult = JsonRpcResponse.JsonRpcResult(id, result = result) - -@JvmSynthetic -internal fun Sign.Model.JsonRpcResponse.JsonRpcError.toRpcError(): JsonRpcResponse.JsonRpcError = JsonRpcResponse.JsonRpcError(id, error = JsonRpcResponse.Error(code, message)) - -@JvmSynthetic -internal fun Sign.Model.SessionEvent.toEngineEvent(chainId: String): EngineDO.Event = EngineDO.Event(name, data, chainId) - -@JvmSynthetic -internal fun EngineDO.SessionDelete.toClientDeletedSession(): Sign.Model.DeletedSession = - Sign.Model.DeletedSession.Success(topic, reason) - -@JvmSynthetic -internal fun EngineDO.SessionEvent.toClientSessionEvent(): Sign.Model.SessionEvent = - Sign.Model.SessionEvent(name, data) - -@JvmSynthetic -internal fun EngineDO.SessionAuthenticateEvent.toClientSessionAuthenticate(): Sign.Model.SessionAuthenticate = with(payloadParams) { - Sign.Model.SessionAuthenticate( - id, - pairingTopic, - participant.toClient(), - payloadParams.toClient(), - expiryTimestamp - ) -} - -@JvmSynthetic -internal fun EngineDO.Participant.toClient(): Sign.Model.SessionAuthenticate.Participant = Sign.Model.SessionAuthenticate.Participant(publicKey, metadata.toClient()) - -@JvmSynthetic -internal fun EngineDO.PayloadParams.toClient(): Sign.Model.PayloadParams = Sign.Model.PayloadParams( - type = type, - chains = chains, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat -) - -@JvmSynthetic -internal fun EngineDO.SessionEvent.toClientEvent(): Sign.Model.Event = - Sign.Model.Event(topic, name, data, chainId) - -@JvmSynthetic -internal fun EngineDO.Session.toClientActiveSession(): Sign.Model.Session = - Sign.Model.Session( - pairingTopic, - topic.value, - expiry.seconds, - requiredNamespaces.toMapOfClientNamespacesProposal(), - optionalNamespaces?.toMapOfClientNamespacesProposal(), - namespaces.toMapOfClientNamespacesSession(), - peerAppMetaData?.toClient() - ) - -@JvmSynthetic -internal fun EngineDO.SessionExtend.toClientActiveSession(): Sign.Model.Session = - Sign.Model.Session( - pairingTopic, - topic.value, - expiry.seconds, - requiredNamespaces.toMapOfClientNamespacesProposal(), - optionalNamespaces?.toMapOfClientNamespacesProposal(), - namespaces.toMapOfClientNamespacesSession(), - peerAppMetaData?.toClient() - ) - -@JvmSynthetic -internal fun EngineDO.SessionRejected.toClientSessionRejected(): Sign.Model.RejectedSession = - Sign.Model.RejectedSession(topic, reason) - -@JvmSynthetic -internal fun EngineDO.SessionApproved.toClientSessionApproved(): Sign.Model.ApprovedSession = - Sign.Model.ApprovedSession(topic, peerAppMetaData?.toClient(), namespaces.toMapOfClientNamespacesSession(), accounts) - -@JvmSynthetic -internal fun Map.toMapOfClientNamespacesSession(): Map = - this.mapValues { (_, namespace) -> - Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Sign.Params.Request.toEngineDORequest(): EngineDO.Request = - EngineDO.Request(sessionTopic, method, params, chainId, expiry?.let { Expiry(it) }) - -@JvmSynthetic -internal fun Sign.Params.Request.toSentRequest(requestId: Long): Sign.Model.SentRequest = - Sign.Model.SentRequest(requestId, sessionTopic, method, params, chainId) - -@JvmSynthetic -internal fun EngineDO.JsonRpcResponse.JsonRpcResult.toClientJsonRpcResult(): Sign.Model.JsonRpcResponse.JsonRpcResult = - Sign.Model.JsonRpcResponse.JsonRpcResult(id, result) - -@JvmSynthetic -internal fun EngineDO.SessionUpdateNamespaces.toClientSessionsNamespaces(): Sign.Model.UpdatedSession = - Sign.Model.UpdatedSession(topic.value, namespaces.toMapOfClientNamespacesSession()) - -@JvmSynthetic -internal fun EngineDO.JsonRpcResponse.JsonRpcError.toClientJsonRpcError(): Sign.Model.JsonRpcResponse.JsonRpcError = - Sign.Model.JsonRpcResponse.JsonRpcError(id, code = error.code, message = error.message) - -@JvmSynthetic -internal fun EngineDO.PairingSettle.toClientSettledPairing(): Sign.Model.Pairing = - Sign.Model.Pairing(topic.value, appMetaData?.toClient()) - -@JvmSynthetic -internal fun List>.mapToPendingRequests(): List = map { request -> - Sign.Model.PendingRequest( - request.id, - request.topic.value, - request.method, - request.chainId, - request.params - ) -} - -@JvmSynthetic -internal fun List.mapToPendingSessionRequests(): List = - map { request -> request.toClientSessionRequest() } - -@JvmSynthetic -internal fun Request.toClient(): Sign.Model.SessionAuthenticate = - Sign.Model.SessionAuthenticate( - id = id, - topic = topic.value, - participant = Sign.Model.SessionAuthenticate.Participant(params.requester.publicKey, params.requester.metadata.toClient()), - payloadParams = params.toClient(), - expiry = params.expiryTimestamp - ) - -@JvmSynthetic -internal fun SignParams.SessionAuthenticateParams.toClient(): Sign.Model.PayloadParams = with(this.authPayload) { - Sign.Model.PayloadParams( - type = type, - chains = chains, - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - iat = iat - ) -} - -@JvmSynthetic -internal fun EngineDO.SessionPayloadResponse.toClientSessionPayloadResponse(): Sign.Model.SessionRequestResponse = - Sign.Model.SessionRequestResponse(topic, chainId, method, result.toClientJsonRpcResponse()) - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesRequired(): Map = - mapValues { (_, namespace) -> - EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesOptional(): Map = - mapValues { (_, namespace) -> - EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfClientNamespacesProposal(): Map = - mapValues { (_, namespace) -> - Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesSession(): Map = - mapValues { (_, namespace) -> - EngineDO.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toProposalNamespacesVO(): Map = - mapValues { (_, namespace) -> - Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) - } - -@JvmSynthetic -internal fun Map.toSessionNamespacesVO(): Map = - mapValues { (_, namespace) -> - Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toCore(): Map = - mapValues { (_, namespace) -> - Sign.Model.Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toSign(): Map = - mapValues { (_, namespace) -> - Sign.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun ConnectionState.toClientConnectionState(): Sign.Model.ConnectionState = - Sign.Model.ConnectionState(isAvailable) - -@JvmSynthetic -internal fun EngineDO.ExpiredProposal.toClient(): Sign.Model.ExpiredProposal = - Sign.Model.ExpiredProposal(pairingTopic, proposerPublicKey) - -@JvmSynthetic -internal fun EngineDO.ExpiredRequest.toClient(): Sign.Model.ExpiredRequest = - Sign.Model.ExpiredRequest(topic, id) - -@JvmSynthetic -internal fun SDKError.toClientError(): Sign.Model.Error = - Sign.Model.Error(this.exception) - -@JvmSynthetic -internal fun Core.Model.Message.SessionProposal.toSign(): Sign.Model.Message.SessionProposal = - Sign.Model.Message.SessionProposal( - id, - pairingTopic, - name, - description, - url, - icons, - redirect, - requiredNamespaces.toSign(), - optionalNamespaces.toSign(), - properties, - proposerPublicKey, - relayProtocol, - relayData - ) - -@JvmSynthetic -internal fun Core.Model.Message.SessionAuthenticate.toSign(): Sign.Model.Message.SessionAuthenticate = - Sign.Model.Message.SessionAuthenticate( - id, topic, metadata, payloadParams.toClient(), expiry - ) - -private fun Core.Model.Message.SessionAuthenticate.PayloadParams.toClient(): Sign.Model.PayloadParams { - return with(this) { - Sign.Model.PayloadParams( - chains = chains, - domain = domain, - nonce = nonce, - aud = aud, - type = type, - nbf = nbf, - exp = exp, - iat = iat, - statement = requestId, - resources = resources, - requestId = requestId, - ) - } -} - -@JvmSynthetic -internal fun Core.Model.Message.SessionRequest.toSign(): Sign.Model.Message.SessionRequest = - Sign.Model.Message.SessionRequest( - topic, - chainId, - peerMetaData, - Sign.Model.Message.SessionRequest.JSONRPCRequest(request.id, request.method, request.params) - ) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/CacaoSigner.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/CacaoSigner.kt deleted file mode 100644 index 6273140fd..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/CacaoSigner.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.walletconnect.sign.client.utils - -import com.walletconnect.android.cacao.signature.ISignatureType -import com.walletconnect.android.utils.cacao.CacaoSignerInterface -import com.walletconnect.sign.client.Sign - -/** - * @deprecated Only added to have backwards compatibility. Newer SDKs should only add CacaoSigner object below. - */ - -@Deprecated("Moved to android-core module, as other SDKs also need CACAO.", ReplaceWith("com.walletconnect.android.internal.common.cacao.signature.SignatureType")) -enum class SignatureType(override val header: String) : ISignatureType { - EIP191("eip191"), EIP1271("eip1271"); -} - -object CacaoSigner : CacaoSignerInterface \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/Utils.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/Utils.kt deleted file mode 100644 index ec62a64f9..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/client/utils/Utils.kt +++ /dev/null @@ -1,153 +0,0 @@ -@file:JvmName("ApprovedNamespacesUtils") - -package com.walletconnect.sign.client.utils - -import android.util.Base64 -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.signing.cacao.Cacao.Payload.Companion.RECAPS_PREFIX -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.RECAPS_STATEMENT -import com.walletconnect.android.internal.common.signing.cacao.decodeReCaps -import com.walletconnect.android.internal.common.signing.cacao.getStatement -import com.walletconnect.android.internal.common.signing.cacao.parseReCaps -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.mapper.toCacaoPayload -import com.walletconnect.sign.client.mapper.toCore -import com.walletconnect.sign.client.mapper.toProposalNamespacesVO -import com.walletconnect.sign.client.mapper.toSessionNamespacesVO -import com.walletconnect.sign.common.validator.SignValidator -import org.json.JSONArray -import org.json.JSONObject - -fun generateApprovedNamespaces( - proposal: Sign.Model.SessionProposal, - supportedNamespaces: Map, -): Map { - val supportedNamespacesVO = supportedNamespaces.toSessionNamespacesVO() - val normalizedRequiredNamespaces = normalizeNamespaces(proposal.requiredNamespaces.toProposalNamespacesVO()) - val normalizedOptionalNamespaces = normalizeNamespaces(proposal.optionalNamespaces.toProposalNamespacesVO()) - - SignValidator.validateProposalNamespaces(normalizedRequiredNamespaces) { error -> throw Exception(error.message) } - SignValidator.validateProposalNamespaces(normalizedOptionalNamespaces) { error -> throw Exception(error.message) } - SignValidator.validateSupportedNamespace(supportedNamespacesVO, normalizedRequiredNamespaces) { error -> throw Exception(error.message) } - - if (proposal.requiredNamespaces.isEmpty() && proposal.optionalNamespaces.isEmpty()) { - return supportedNamespacesVO.toCore() - } - - val approvedNamespaces = mutableMapOf() - normalizedRequiredNamespaces.forEach { (key, requiredNamespace) -> - val chains = supportedNamespacesVO[key]?.chains?.filter { chain -> requiredNamespace.chains!!.contains(chain) } ?: emptyList() - val methods = supportedNamespaces[key]?.methods?.filter { method -> requiredNamespace.methods.contains(method) } ?: emptyList() - val events = supportedNamespaces[key]?.events?.filter { event -> requiredNamespace.events.contains(event) } ?: emptyList() - val accounts = chains.flatMap { chain -> supportedNamespaces[key]?.accounts?.filter { account -> SignValidator.getChainFromAccount(account) == chain } ?: emptyList() } - - approvedNamespaces[key] = Namespace.Session(chains = chains, methods = methods, events = events, accounts = accounts) - } - - normalizedOptionalNamespaces.forEach { (key, optionalNamespace) -> - if (!supportedNamespaces.containsKey(key)) return@forEach - val chains = supportedNamespacesVO[key]?.chains?.filter { chain -> optionalNamespace.chains!!.contains(chain) } ?: emptyList() - val methods = supportedNamespaces[key]?.methods?.filter { method -> optionalNamespace.methods.contains(method) } ?: emptyList() - val events = supportedNamespaces[key]?.events?.filter { event -> optionalNamespace.events.contains(event) } ?: emptyList() - val accounts = chains.flatMap { chain -> supportedNamespaces[key]?.accounts?.filter { account -> SignValidator.getChainFromAccount(account) == chain } ?: emptyList() } - - approvedNamespaces[key] = Namespace.Session( - chains = approvedNamespaces[key]?.chains?.plus(chains)?.distinct() ?: chains, - methods = approvedNamespaces[key]?.methods?.plus(methods)?.distinct() ?: methods, - events = approvedNamespaces[key]?.events?.plus(events)?.distinct() ?: events, - accounts = approvedNamespaces[key]?.accounts?.plus(accounts)?.distinct() ?: accounts - ) - } - - return approvedNamespaces.toCore() -} - -internal fun normalizeNamespaces(namespaces: Map): Map { - if (SignValidator.isNamespaceKeyRegexCompliant(namespaces)) return namespaces - return mutableMapOf().apply { - namespaces.forEach { (key, namespace) -> - val normalizedKey = normalizeKey(key) - this[normalizedKey] = Namespace.Proposal( - chains = getChains(normalizedKey).plus(getNamespaceChains(key, namespace)), - methods = getMethods(normalizedKey).plus(namespace.methods), - events = getEvents(normalizedKey).plus(namespace.events) - ) - } - }.toMap() -} - -private fun getNamespaceChains(key: String, namespace: Namespace) = if (CoreValidator.isChainIdCAIP2Compliant(key)) listOf(key) else namespace.chains!! -private fun normalizeKey(key: String): String = if (CoreValidator.isChainIdCAIP2Compliant(key)) SignValidator.getNamespaceKeyFromChainId(key) else key -private fun MutableMap.getChains(normalizedKey: String) = (this[normalizedKey]?.chains ?: emptyList()) -private fun MutableMap.getMethods(normalizedKey: String) = (this[normalizedKey]?.methods ?: emptyList()) -private fun MutableMap.getEvents(normalizedKey: String) = (this[normalizedKey]?.events ?: emptyList()) - -fun generateAuthObject(payload: Sign.Model.PayloadParams, issuer: String, signature: Sign.Model.Cacao.Signature): Sign.Model.Cacao { - return Sign.Model.Cacao( - header = Sign.Model.Cacao.Header(t = CacaoType.CAIP222.header), - payload = payload.toCacaoPayload(issuer), - signature = signature - ) -} - -fun generateAuthPayloadParams(payloadParams: Sign.Model.PayloadParams, supportedChains: List, supportedMethods: List): Sign.Model.PayloadParams { - val reCapsJson: String? = payloadParams.resources.decodeReCaps() - if (reCapsJson.isNullOrEmpty() || !reCapsJson.contains("eip155")) return payloadParams - - val sessionReCaps = reCapsJson.parseReCaps()["eip155"] - val requestedMethods = sessionReCaps!!.keys.map { key -> key.substringAfter('/') } - val requestedChains = payloadParams.chains - - val sessionChains = requestedChains.intersect(supportedChains.toSet()).toList().distinct() - val sessionMethods = requestedMethods.intersect(supportedMethods.toSet()).toList().distinct() - - if (sessionChains.isEmpty()) throw Exception("Unsupported chains") - if (sessionMethods.isEmpty()) throw Exception("Unsupported methods") - - if (!sessionChains.all { chain -> CoreValidator.isChainIdCAIP2Compliant(chain) }) throw Exception("Chains are not CAIP-2 compliant") - if (!sessionChains.all { chain -> SignValidator.getNamespaceKeyFromChainId(chain) == "eip155" }) throw Exception("Only eip155(EVM) is supported") - - val actionsJsonObject = JSONObject() - val chainsJsonArray = JSONArray() - sessionChains.forEach { chain -> chainsJsonArray.put(chain) } - sessionMethods.forEach { method -> actionsJsonObject.put("request/$method", JSONArray().put(0, JSONObject().put("chains", chainsJsonArray))) } - - val recaps = JSONObject(reCapsJson) - val att = recaps.getJSONObject("att") - att.put("eip155", actionsJsonObject) - val stringReCaps = recaps.toString().replace("\\/", "/") - val base64Recaps = Base64.encodeToString(stringReCaps.toByteArray(Charsets.UTF_8), Base64.NO_WRAP or Base64.NO_PADDING) - val newReCapsUrl = "${RECAPS_PREFIX}$base64Recaps" - - if (payloadParams.resources == null) { - payloadParams.resources = listOf(newReCapsUrl) - } else { - payloadParams.resources = payloadParams.resources!!.dropLast(1).plus(newReCapsUrl) - } - - return with(payloadParams) { - Sign.Model.PayloadParams( - chains = sessionChains, - domain = domain, - nonce = nonce, - aud = aud, - type = type, - nbf = nbf, - exp = exp, - iat = iat, - statement = getStatement(), - resources = resources, - requestId = requestId - ) - } -} - -private fun Sign.Model.PayloadParams.getStatement() = - if (statement?.contains(RECAPS_STATEMENT) == true) { - statement - } else { - Pair(statement, resources).getStatement() - } - diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/Messages.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/Messages.kt deleted file mode 100644 index 917b5f138..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/Messages.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.walletconnect.sign.common.exceptions - -internal const val NO_SEQUENCE_FOR_TOPIC_MESSAGE: String = "Cannot find sequence for given topic: " -internal const val UNAUTHORIZED_UPDATE_MESSAGE: String = - "The update() was called by the unauthorized peer. Must be called by controller client." -internal const val UNAUTHORIZED_EXTEND_MESSAGE: String = - "The extend() was called by the unauthorized peer. Must be called by controller client." -internal const val UNAUTHORIZED_EMIT_MESSAGE: String = - "The emit() was called by the unauthorized peer. Must be called by controller client." -internal const val SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE: String = "Session is not acknowledged, topic: " - -internal const val EMPTY_NAMESPACES_MESSAGE: String = "Session namespaces MUST not be empty" -internal const val NAMESPACE_CHAINS_MISSING_MESSAGE: String = "Chains must not be empty" -internal const val NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE: String = "Chains must not be null" -internal const val NAMESPACE_CHAINS_CAIP_2_MESSAGE: String = "Chains must be CAIP-2 compliant" -internal const val NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE: String = "Chains must be defined in matching namespace" -internal const val NAMESPACE_KEYS_INVALID_FORMAT: String = "Invalid namespace id format" - -internal const val NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE: String = "Accounts must be CAIP-10 compliant" -internal const val NAMESPACE_METHODS_MISSING_MESSAGE: String = "All required namespaces must be approved: not all methods are approved" -internal const val NAMESPACE_EVENTS_MISSING_MESSAGE: String = "All events must be approved: not all events are approved" -internal const val NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE: String = "Accounts must be defined in matching namespace" -internal const val NAMESPACE_KEYS_MISSING_MESSAGE: String = "All required namespaces must be approved" - -internal const val UNAUTHORIZED_METHOD_MESSAGE: String = "Unauthorized method is not authorized for given chain" -internal const val UNAUTHORIZED_EVENT_MESSAGE: String = "Unauthorized event is not authorized for given chain" -internal const val INVALID_EVENT_MESSAGE: String = "Event name and data fields cannot be empty. ChainId must be CAIP-2 compliant" -internal const val INVALID_REQUEST_MESSAGE: String = "Request topic, method and params fields cannot be empty. ChainId must be CAIP-2 compliant" -internal const val INVALID_EXTEND_TIME: String = "Extend time is out of range" -internal const val INVALID_SESSION_PROPERTIES: String = "Invalid Session Properties requested" - -internal const val CLIENT_ALREADY_INITIALIZED: String = "SignClient already initialized" -internal const val INVALID_SIGN_PARAMS_TYPE: String = "Invalid Sign params type" - -internal const val MISSING_SESSION_AUTHENTICATE_REQUEST: String = "Missing session authenticate request, expired" -internal const val INVALID_CACAO_EXCEPTION: String = "Invalid Cacao exception" \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/PeerError.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/PeerError.kt deleted file mode 100644 index 0b5cc74ca..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/exceptions/PeerError.kt +++ /dev/null @@ -1,128 +0,0 @@ -package com.walletconnect.sign.common.exceptions - -import com.walletconnect.android.internal.common.model.type.Error - -/** - * Documentation: https://github.com/WalletConnect/walletconnect-specs/blob/main/sign/error-codes.md - */ -sealed class PeerError : Error { - sealed class Invalid : PeerError() { - - data class Method(val reason: String) : Invalid() { - override val message: String = "Invalid session request: $reason" - override val code: Int = 1001 - } - - data class Event(val reason: String) : Invalid() { - override val message: String = "Invalid event request: $reason" - override val code: Int = 1002 - } - - data class UpdateRequest(val reason: String) : Invalid() { - override val message: String = "Invalid update namespace request: $reason" - override val code: Int = 1003 - } - - data class ExtendRequest(val reason: String) : Invalid() { - override val message = "Invalid session extend request: $reason" - override val code: Int = 1004 - } - - data class SessionSettleRequest(override val message: String) : Invalid() { - override val code: Int = 1005 - } - } - - sealed class Unauthorized : PeerError() { - - data class Method(val reason: String) : Unauthorized() { - override val message: String = "Unauthorized session request: $reason" - override val code: Int = 3001 - } - - data class Event(val reason: String) : Unauthorized() { - override val message: String = "Unauthorized event request: $reason" - override val code: Int = 3002 - } - - data class UpdateRequest(val sequence: String) : Unauthorized() { - override val message: String = "Unauthorized update $sequence namespace request" - override val code: Int = 3003 - } - - data class ExtendRequest(val sequence: String) : Unauthorized() { - override val message: String = "Unauthorized $sequence extend request" - override val code: Int = 3004 - } - - data class Chain(override val message: String) : Unauthorized() { - override val code: Int = 3005 - } - } - - sealed class EIP1193 : PeerError() { - - data class UserRejectedRequest(override val message: String) : EIP1193() { - override val code: Int = 4001 - } - } - - sealed class CAIP25 : PeerError() { - - data class UserRejected(override val message: String) : CAIP25() { - override val code: Int = 5000 - } - - data class UserRejectedChains(override val message: String) : CAIP25() { - override val code: Int = 5001 - } - - data class UserRejectedMethods(override val message: String) : CAIP25() { - override val code: Int = 5002 - } - - data class UserRejectedEvents(override val message: String) : CAIP25() { - override val code: Int = 5003 - } - - data class UnsupportedChains(override val message: String) : CAIP25() { - override val code: Int = 5100 - } - - data class UnsupportedMethods(override val message: String) : CAIP25() { - override val code: Int = 5101 - } - - data class UnsupportedEvents(override val message: String) : CAIP25() { - override val code: Int = 5102 - } - - data class UnsupportedAccounts(override val message: String) : CAIP25() { - override val code: Int = 5103 - } - - data class UnsupportedNamespaceKey(override val message: String) : CAIP25() { - override val code: Int = 5104 - } - - data class InvalidSessionPropertiesObject(override val message: String) : CAIP25() { - override val code: Int = 5200 - } - - data class EmptySessionNamespaces(override val message: String) : CAIP25() { - override val code: Int = 5201 - } - } - - sealed class Failure : PeerError() { - - data class SessionSettlementFailed(val reason: String) : Failure() { - override val message: String = "Invalid Session Settle Request: $reason" - override val code: Int = 7000 - } - - data class NoSessionForTopic(override val message: String) : Failure() { - override val code: Int = 7001 - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/Request.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/Request.kt deleted file mode 100644 index d6b65e586..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/Request.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.sign.common.model - -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.foundation.common.model.Topic - -internal data class Request( - val id: Long, - val topic: Topic, - val method: String, - val chainId: String?, - val params: T, - val expiry: Expiry? = null, - val transportType: TransportType? -) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/type/Sequences.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/type/Sequences.kt deleted file mode 100644 index d27c5815a..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/type/Sequences.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.sign.common.model.type - -enum class Sequences { - SESSION, PAIRING -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/PayloadParams.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/PayloadParams.kt deleted file mode 100644 index 5915abc63..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/PayloadParams.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.walletconnect.sign.common.model.vo.clientsync.common - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -internal data class PayloadParams( - @Json(name = "type") - val type: String, - @Json(name = "chains") - val chains: List, - @Json(name = "domain") - val domain: String, - @Json(name = "aud") - val aud: String, - @Json(name = "nonce") - val nonce: String, - @Json(name = "version") - val version: String, - @Json(name = "iat") - val iat: String, - @Json(name = "nbf") - val nbf: String?, - @Json(name = "exp") - val exp: String?, - @Json(name = "statement") - val statement: String?, - @Json(name = "requestId") - val requestId: String?, - @Json(name = "resources") - val resources: List?, -) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/Requester.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/Requester.kt deleted file mode 100644 index 140f93982..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/Requester.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.sign.common.model.vo.clientsync.common - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.AppMetaData - -@JsonClass(generateAdapter = true) -data class Requester( - @Json(name = "publicKey") - val publicKey: String, - @Json(name = "metadata") - val metadata: AppMetaData -) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/SessionParticipant.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/SessionParticipant.kt deleted file mode 100644 index e1fa6af5a..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/clientsync/common/SessionParticipant.kt +++ /dev/null @@ -1,15 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.common.model.vo.clientsync.common - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.AppMetaData - -@JsonClass(generateAdapter = true) -internal data class SessionParticipant( - @Json(name = "publicKey") - val publicKey: String, - @Json(name = "metadata") - val metadata: AppMetaData, -) diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/proposal/ProposalVO.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/proposal/ProposalVO.kt deleted file mode 100644 index 725070f7f..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/common/model/vo/proposal/ProposalVO.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.walletconnect.sign.common.model.vo.proposal - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.foundation.common.model.Topic - -internal data class ProposalVO( - val requestId: Long, - val pairingTopic: Topic, - val name: String, - val description: String, - val url: String, - val icons: List, - val redirect: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map, - val properties: Map?, - val proposerPublicKey: String, - val relayProtocol: String, - val relayData: String?, - val expiry: Expiry? -) { - val appMetaData: AppMetaData - get() = AppMetaData(name = name, description = description, url = url, icons = icons, redirect = Redirect(native = redirect)) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/CallsModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/CallsModule.kt deleted file mode 100644 index 454e431f0..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/CallsModule.kt +++ /dev/null @@ -1,210 +0,0 @@ -package com.walletconnect.sign.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionUseCase -import com.walletconnect.sign.engine.use_case.calls.ApproveSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.DecryptSignMessageUseCase -import com.walletconnect.sign.engine.use_case.calls.DisconnectSessionUseCase -import com.walletconnect.sign.engine.use_case.calls.DisconnectSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.EmitEventUseCase -import com.walletconnect.sign.engine.use_case.calls.EmitEventUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ExtendSessionUseCase -import com.walletconnect.sign.engine.use_case.calls.ExtendSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.FormatAuthenticateMessageUseCase -import com.walletconnect.sign.engine.use_case.calls.FormatAuthenticateMessageUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetListOfVerifyContextsUseCase -import com.walletconnect.sign.engine.use_case.calls.GetListOfVerifyContextsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetNamespacesFromReCaps -import com.walletconnect.sign.engine.use_case.calls.GetPairingForSessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.calls.GetPairingsUseCase -import com.walletconnect.sign.engine.use_case.calls.GetPairingsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetSessionProposalsUseCase -import com.walletconnect.sign.engine.use_case.calls.GetSessionProposalsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetSessionsUseCase -import com.walletconnect.sign.engine.use_case.calls.GetSessionsUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.GetVerifyContextByIdUseCase -import com.walletconnect.sign.engine.use_case.calls.GetVerifyContextByIdUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.PairUseCase -import com.walletconnect.sign.engine.use_case.calls.PairUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.PingUseCase -import com.walletconnect.sign.engine.use_case.calls.PingUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.ProposeSessionUseCase -import com.walletconnect.sign.engine.use_case.calls.ProposeSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RejectSessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.calls.RejectSessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RejectSessionUseCase -import com.walletconnect.sign.engine.use_case.calls.RejectSessionUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.RespondSessionRequestUseCase -import com.walletconnect.sign.engine.use_case.calls.RespondSessionRequestUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.calls.SessionAuthenticateUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionRequestUseCase -import com.walletconnect.sign.engine.use_case.calls.SessionRequestUseCaseInterface -import com.walletconnect.sign.engine.use_case.calls.SessionUpdateUseCase -import com.walletconnect.sign.engine.use_case.calls.SessionUpdateUseCaseInterface -import com.walletconnect.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopic -import com.walletconnect.sign.json_rpc.domain.GetPendingRequestsUseCaseByTopicInterface -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCase -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionRequestByTopicUseCaseInterface -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun callsModule() = module { - - single { - ProposeSessionUseCase( - jsonRpcInteractor = get(), - crypto = get(), - selfAppMetaData = get(), - proposalStorageRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - SessionAuthenticateUseCase( - jsonRpcInteractor = get(), - crypto = get(), - selfAppMetaData = get(), - authenticateResponseTopicRepository = get(), - proposeSessionUseCase = get(), - getPairingForSessionAuthenticate = get(), - getNamespacesFromReCaps = get(), - linkModeJsonRpcInteractor = get(), - linkModeStorageRepository = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { PairUseCase(pairingInterface = get()) } - - single { - ApproveSessionUseCase( - proposalStorageRepository = get(), - selfAppMetaData = get(), - crypto = get(), - jsonRpcInteractor = get(), - metadataStorageRepository = get(), - sessionStorageRepository = get(), - verifyContextStorageRepository = get(), - insertEventUseCase = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - ApproveSessionAuthenticateUseCase( - jsonRpcInteractor = get(), - crypto = get(), - cacaoVerifier = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - verifyContextStorageRepository = get(), - getPendingSessionAuthenticateRequest = get(), - selfAppMetaData = get(), - sessionStorageRepository = get(), - metadataStorageRepository = get(), - insertTelemetryEventUseCase = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - linkModeJsonRpcInteractor = get() - ) - } - - single { - RejectSessionAuthenticateUseCase( - jsonRpcInteractor = get(), - crypto = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - verifyContextStorageRepository = get(), - getPendingSessionAuthenticateRequest = get(), - linkModeJsonRpcInteractor = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - insertEventUseCase = get() - ) - } - - single { - RejectSessionUseCase( - verifyContextStorageRepository = get(), - proposalStorageRepository = get(), - jsonRpcInteractor = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { SessionUpdateUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { - SessionRequestUseCase( - jsonRpcInteractor = get(), - sessionStorageRepository = get(), - linkModeJsonRpcInteractor = get(), - metadataStorageRepository = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - RespondSessionRequestUseCase( - jsonRpcInteractor = get(), - verifyContextStorageRepository = get(), - sessionStorageRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - getPendingJsonRpcHistoryEntryByIdUseCase = get(), - linkModeJsonRpcInteractor = get(), - metadataStorageRepository = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - ) - } - - single(named(AndroidCommonDITags.DECRYPT_SIGN_MESSAGE)) { - val useCase = DecryptSignMessageUseCase( - codec = get(), - serializer = get(), - metadataRepository = get(), - pushMessageStorage = get(), - ) - - get>(named(AndroidCommonDITags.DECRYPT_USE_CASES))[Tags.SESSION_PROPOSE.id.toString()] = useCase - useCase - } - - single { PingUseCase(sessionStorageRepository = get(), jsonRpcInteractor = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { EmitEventUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { ExtendSessionUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { DisconnectSessionUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { GetSessionsUseCase(sessionStorageRepository = get(), metadataStorageRepository = get(), selfAppMetaData = get()) } - - single { GetPairingsUseCase(pairingInterface = get()) } - - single { GetPairingForSessionAuthenticateUseCase(pairingProtocol = get()) } - - single { GetNamespacesFromReCaps() } - - single { GetPendingRequestsUseCaseByTopic(serializer = get(), jsonRpcHistory = get()) } - - single { GetPendingSessionRequestByTopicUseCase(jsonRpcHistory = get(), serializer = get(), metadataStorageRepository = get()) } - - single { GetSessionProposalsUseCase(proposalStorageRepository = get()) } - - single { GetVerifyContextByIdUseCase(verifyContextStorageRepository = get()) } - - single { GetListOfVerifyContextsUseCase(verifyContextStorageRepository = get()) } - - single { FormatAuthenticateMessageUseCase() } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/EngineModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/EngineModule.kt deleted file mode 100644 index 2f29c4c24..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/EngineModule.kt +++ /dev/null @@ -1,95 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.sign.engine.domain.SignEngine -import com.walletconnect.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCase -import com.walletconnect.sign.engine.use_case.calls.GetPendingAuthenticateRequestUseCaseInterface -import com.walletconnect.sign.json_rpc.domain.DeleteRequestByIdUseCase -import com.walletconnect.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionRequests -import com.walletconnect.sign.json_rpc.domain.GetSessionAuthenticateRequest -import com.walletconnect.sign.json_rpc.domain.GetSessionRequestByIdUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun engineModule() = module { - - includes(callsModule(), requestsModule(), responsesModule()) - - single { GetPendingSessionRequests(jsonRpcHistory = get(), serializer = get()) } - - single { GetPendingAuthenticateRequestUseCase(jsonRpcHistory = get(), serializer = get()) } - - single { DeleteRequestByIdUseCase(jsonRpcHistory = get(), verifyContextStorageRepository = get()) } - - single { GetPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcHistory = get(), serializer = get()) } - - single { GetSessionRequestByIdUseCase(jsonRpcHistory = get(), serializer = get()) } - - single { GetPendingSessionAuthenticateRequest(jsonRpcHistory = get(), serializer = get()) } - - single { GetSessionAuthenticateRequest(jsonRpcHistory = get(), serializer = get()) } - - single { CacaoVerifier(projectId = get()) } - - single { - SignEngine( - verifyContextStorageRepository = get(), - jsonRpcInteractor = get(), - crypto = get(), - authenticateResponseTopicRepository = get(), - proposalStorageRepository = get(), - authenticateSessionUseCase = get(), - sessionStorageRepository = get(), - metadataStorageRepository = get(), - approveSessionUseCase = get(), - disconnectSessionUseCase = get(), - emitEventUseCase = get(), - extendSessionUseCase = get(), - decryptMessageUseCase = get(named(AndroidCommonDITags.DECRYPT_SIGN_MESSAGE)), - getListOfVerifyContextsUseCase = get(), - getPairingsUseCase = get(), - getPendingRequestsByTopicUseCase = get(), - getPendingSessionRequests = get(), - getSessionProposalsUseCase = get(), - getSessionsUseCase = get(), - onPingUseCase = get(), - getVerifyContextByIdUseCase = get(), - onSessionDeleteUseCase = get(), - onSessionEventUseCase = get(), - onSessionExtendUseCase = get(), - getPendingSessionRequestByTopicUseCase = get(), - onSessionProposalResponseUseCase = get(), - onSessionProposeUse = get(), - onSessionRequestResponseUseCase = get(), - onSessionRequestUseCase = get(), - onSessionSettleResponseUseCase = get(), - onSessionSettleUseCase = get(), - onSessionUpdateResponseUseCase = get(), - onSessionUpdateUseCase = get(), - pairingController = get(), - pairUseCase = get(), - pingUseCase = get(), - proposeSessionUseCase = get(), - rejectSessionUseCase = get(), - respondSessionRequestUseCase = get(), - sessionRequestUseCase = get(), - sessionUpdateUseCase = get(), - onAuthenticateSessionUseCase = get(), - onSessionAuthenticateResponseUseCase = get(), - approveSessionAuthenticateUseCase = get(), - rejectSessionAuthenticateUseCase = get(), - formatAuthenticateMessageUseCase = get(), - deleteRequestByIdUseCase = get(), - getPendingAuthenticateRequestUseCase = get(), - insertEventUseCase = get(), - linkModeJsonRpcInteractor = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/RequestsModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/RequestsModule.kt deleted file mode 100644 index 1b5085a4b..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/RequestsModule.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.walletconnect.sign.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.sign.engine.use_case.requests.OnPingUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionAuthenticateUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionDeleteUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionEventUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionExtendUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionProposalUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionRequestUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionSettleUseCase -import com.walletconnect.sign.engine.use_case.requests.OnSessionUpdateUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun requestsModule() = module { - - single { - OnSessionProposalUseCase( - pairingController = get(), - jsonRpcInteractor = get(), - proposalStorageRepository = get(), - resolveAttestationIdUseCase = get(), - insertEventUseCase = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - OnSessionAuthenticateUseCase( - jsonRpcInteractor = get(), - resolveAttestationIdUseCase = get(), - logger = get(), - pairingController = get(), - insertTelemetryEventUseCase = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - ) - } - - single { - OnSessionSettleUseCase( - proposalStorageRepository = get(), - jsonRpcInteractor = get(), - pairingController = get(), - metadataStorageRepository = get(), - sessionStorageRepository = get(), - crypto = get(), - selfAppMetaData = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - OnSessionRequestUseCase( - metadataStorageRepository = get(), - sessionStorageRepository = get(), - jsonRpcInteractor = get(), - resolveAttestationIdUseCase = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - ) - } - - single { OnSessionDeleteUseCase(jsonRpcInteractor = get(), crypto = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { OnSessionEventUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { OnSessionUpdateUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { OnSessionExtendUseCase(jsonRpcInteractor = get(), sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { OnPingUseCase(jsonRpcInteractor = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/ResponsesModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/ResponsesModule.kt deleted file mode 100644 index 46892396a..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/ResponsesModule.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.walletconnect.sign.di - -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionAuthenticateResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionProposalResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionRequestResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionSettleResponseUseCase -import com.walletconnect.sign.engine.use_case.responses.OnSessionUpdateResponseUseCase -import org.koin.core.qualifier.named -import org.koin.dsl.module - -@JvmSynthetic -internal fun responsesModule() = module { - - single { - OnSessionProposalResponseUseCase( - jsonRpcInteractor = get(), - crypto = get(), - pairingController = get(), - proposalStorageRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - OnSessionSettleResponseUseCase( - crypto = get(), - jsonRpcInteractor = get(), - sessionStorageRepository = get(), - metadataStorageRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)) - ) - } - - single { - OnSessionAuthenticateResponseUseCase( - pairingController = get(), - pairingInterface = get(), - cacaoVerifier = get(), - sessionStorageRepository = get(), - crypto = get(), - jsonRpcInteractor = get(), - authenticateResponseTopicRepository = get(), - logger = get(named(AndroidCommonDITags.LOGGER)), - getSessionAuthenticateRequest = get(), - metadataStorageRepository = get(), - linkModeStorageRepository = get(), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - ) - } - - single { OnSessionUpdateResponseUseCase(sessionStorageRepository = get(), logger = get(named(AndroidCommonDITags.LOGGER))) } - - single { - OnSessionRequestResponseUseCase( - logger = get(named(AndroidCommonDITags.LOGGER)), - insertEventUseCase = get(), - clientId = get(named(AndroidCommonDITags.CLIENT_ID)), - getSessionRequestByIdUseCase = get() - ) - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignDITags.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignDITags.kt deleted file mode 100644 index 1382f27ea..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignDITags.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.di - -internal enum class SignDITags { - SERIALIZER_SET, - DESERIALIZER_MAP -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignJsonRpcModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignJsonRpcModule.kt deleted file mode 100644 index be33cdb9e..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/SignJsonRpcModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.di - -import com.walletconnect.sign.common.adapters.SessionEventVOJsonAdapter -import com.walletconnect.sign.common.adapters.SessionRequestVOJsonAdapter -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionEventVO -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.utils.addDeserializerEntry -import com.walletconnect.utils.addJsonAdapter -import com.walletconnect.utils.addSerializerEntry -import org.koin.dsl.module - -@JvmSynthetic -internal fun signJsonRpcModule() = module { - addSerializerEntry(SignRpc.SessionPropose::class) - addSerializerEntry(SignRpc.SessionPing::class) - addSerializerEntry(SignRpc.SessionEvent::class) - addSerializerEntry(SignRpc.SessionUpdate::class) - addSerializerEntry(SignRpc.SessionRequest::class) - addSerializerEntry(SignRpc.SessionDelete::class) - addSerializerEntry(SignRpc.SessionSettle::class) - addSerializerEntry(SignRpc.SessionExtend::class) - addSerializerEntry(SignRpc.SessionAuthenticate::class) - - addDeserializerEntry(JsonRpcMethod.WC_SESSION_PROPOSE, SignRpc.SessionPropose::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_SETTLE, SignRpc.SessionSettle::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_REQUEST, SignRpc.SessionRequest::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_DELETE, SignRpc.SessionDelete::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_PING, SignRpc.SessionPing::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_EVENT, SignRpc.SessionEvent::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_UPDATE, SignRpc.SessionUpdate::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_EXTEND, SignRpc.SessionExtend::class) - addDeserializerEntry(JsonRpcMethod.WC_SESSION_AUTHENTICATE, SignRpc.SessionAuthenticate::class) - - addJsonAdapter(SessionEventVO::class.java, ::SessionEventVOJsonAdapter) - addJsonAdapter(SessionRequestVO::class.java, ::SessionRequestVOJsonAdapter) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/StorageModule.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/StorageModule.kt deleted file mode 100644 index d9dbb4455..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/di/StorageModule.kt +++ /dev/null @@ -1,139 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.di - -import com.walletconnect.android.di.sdkBaseStorageModule -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.di.deleteDatabase -import com.walletconnect.sign.SignDatabase -import com.walletconnect.sign.storage.authenticate.AuthenticateResponseTopicRepository -import com.walletconnect.sign.storage.data.dao.namespace.NamespaceDao -import com.walletconnect.sign.storage.data.dao.optionalnamespaces.OptionalNamespaceDao -import com.walletconnect.sign.storage.data.dao.proposal.ProposalDao -import com.walletconnect.sign.storage.data.dao.proposalnamespace.ProposalNamespaceDao -import com.walletconnect.sign.storage.data.dao.session.SessionDao -import com.walletconnect.sign.storage.data.dao.temp.TempNamespaceDao -import com.walletconnect.sign.storage.link_mode.LinkModeStorageRepository -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.launch -import org.koin.core.module.Module -import org.koin.core.qualifier.named -import org.koin.core.scope.Scope -import org.koin.dsl.module -import com.walletconnect.android.internal.common.scope as wcScope - -@JvmSynthetic -internal fun storageModule(dbName: String): Module = module { - includes(sdkBaseStorageModule(SignDatabase.Schema, dbName)) - - fun Scope.createSignDB(): SignDatabase = SignDatabase( - driver = get(named(dbName)), - NamespaceDaoAdapter = NamespaceDao.Adapter( - accountsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) - ), - TempNamespaceDaoAdapter = TempNamespaceDao.Adapter( - accountsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) - ), - ProposalNamespaceDaoAdapter = ProposalNamespaceDao.Adapter( - chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) - ), - OptionalNamespaceDaoAdapter = OptionalNamespaceDao.Adapter( - chainsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - methodsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)), - eventsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) - ), - SessionDaoAdapter = SessionDao.Adapter( - propertiesAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_MAP)), - transport_typeAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_TRANSPORT_TYPE)) - ), - ProposalDaoAdapter = ProposalDao.Adapter( - propertiesAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_MAP)), - iconsAdapter = get(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) - ) - ) - - single { - try { - createSignDB().also { signDatabase -> - wcScope.launch { - try { - signDatabase.sessionDaoQueries.lastInsertedRow().executeAsOneOrNull() - } catch (e: Exception) { - deleteDatabase(dbName) - createSignDB() - } - } - } - } catch (e: Exception) { - deleteDatabase(dbName) - createSignDB() - } - } - - single { - get().sessionDaoQueries - } - - single { - get().namespaceDaoQueries - } - - single { - get().tempNamespaceDaoQueries - } - - single { - get().proposalNamespaceDaoQueries - } - - single { - get().optionalNamespaceDaoQueries - } - - single { - get().proposalDaoQueries - } - - single { - get().authenticateResponseTopicDaoQueries - } - - single { - get().linkModeDaoQueries - } - - single { - SessionStorageRepository( - sessionDaoQueries = get(), - namespaceDaoQueries = get(), - requiredNamespaceDaoQueries = get(), - optionalNamespaceDaoQueries = get(), - tempNamespaceDaoQueries = get() - ) - } - - single { - ProposalStorageRepository( - proposalDaoQueries = get(), - requiredNamespaceDaoQueries = get(), - optionalNamespaceDaoQueries = get() - ) - } - - single { - AuthenticateResponseTopicRepository(authenticateResponseTopicDaoQueries = get()) - } - - single { - LinkModeStorageRepository(linkModeDaoQueries = get()) - } -} diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/SessionRequestQueue.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/SessionRequestQueue.kt deleted file mode 100644 index 2743823ea..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/SessionRequestQueue.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.walletconnect.sign.engine - -import com.walletconnect.sign.engine.model.EngineDO -import java.util.concurrent.ConcurrentLinkedQueue - -internal val sessionRequestEventsQueue = ConcurrentLinkedQueue() \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/EngineDO.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/EngineDO.kt deleted file mode 100644 index d6955b633..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/EngineDO.kt +++ /dev/null @@ -1,245 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.engine.model - -import com.squareup.moshi.JsonClass -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Validation -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.Sequence -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.foundation.common.model.Topic -import java.net.URI -import com.walletconnect.android.internal.common.model.RelayProtocolOptions as CoreRelayProtocolOptions - -internal sealed class EngineDO { - - class WalletConnectUri( - val topic: Topic, - val symKey: SymmetricKey, - val relay: CoreRelayProtocolOptions, - val version: String = "2", - ) : EngineDO() - - data class SessionProposalEvent( - val proposal: SessionProposal, - val context: VerifyContext - ) : EngineDO(), EngineEvent - - data class SessionRequestEvent( - val request: SessionRequest, - val context: VerifyContext - ) : EngineDO(), EngineEvent - - data class SessionAuthenticateEvent( - val id: Long, - val pairingTopic: String, - val payloadParams: PayloadParams, - val participant: Participant, - val expiryTimestamp: Long, - val verifyContext: VerifyContext - ) : EngineDO(), EngineEvent - - data class PayloadParams( - val chains: List, - val domain: String, - val nonce: String, - val aud: String, - val type: String?, - val iat: String, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - var resources: List?, - val version: String - ) : EngineDO() - - data class Authenticate( - val pairingTopic: String? = null, - val chains: List, - val domain: String, - val nonce: String, - val aud: String, - val type: String?, - val nbf: String?, - val exp: String?, - val statement: String?, - val requestId: String?, - var resources: List?, - val methods: List?, - val expiry: Long? - ) : EngineDO() - - data class Participant( - val publicKey: String, - val metadata: AppMetaData, - ) : EngineDO() - - data class SessionProposal( - val pairingTopic: String, - val name: String, - val description: String, - val url: String, - val icons: List, - val redirect: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map, - val properties: Map?, - val proposerPublicKey: String, - val relayProtocol: String, - val relayData: String?, - ) : EngineDO(), EngineEvent - - data class ExpiredProposal(val pairingTopic: String, val proposerPublicKey: String) : EngineDO(), EngineEvent - data class ExpiredRequest(val topic: String, val id: Long) : EngineDO(), EngineEvent - - data class VerifyContext( - val id: Long, - val origin: String, - val validation: Validation, - val verifyUrl: String, - val isScam: Boolean? - ) : EngineDO() - - sealed class Namespace : EngineDO() { - - //Required and Optional - data class Proposal( - val chains: List? = null, - val methods: List, - val events: List - ) : Namespace() - - data class Session( - val chains: List? = null, - val accounts: List, - val methods: List, - val events: List - ) : Namespace() - } - - data class SessionRequest( - val topic: String, - val chainId: String?, - val peerAppMetaData: AppMetaData?, - val request: JSONRPCRequest, - val expiry: Expiry? - ) : EngineDO(), EngineEvent { - - data class JSONRPCRequest( - val id: Long, - val method: String, - val params: String, - ) : EngineDO() - } - - data class SessionPayloadResponse( - val topic: String, - val chainId: String?, - val method: String, - val result: JsonRpcResponse, - ) : EngineDO(), EngineEvent - - data class SessionDelete( - val topic: String, - val reason: String, - ) : EngineDO(), EngineEvent - - data class SessionEvent( - val topic: String, - val name: String, - val data: String, - val chainId: String, //todo: Why was this nullable? - ) : EngineDO(), EngineEvent - - sealed class SessionAuthenticateResponse : EngineDO(), EngineEvent { - data class Result(val id: Long, val cacaos: List, val session: Session?) : SessionAuthenticateResponse() - data class Error(val id: Long, val code: Int, val message: String) : SessionAuthenticateResponse() - } - - sealed class SettledSessionResponse : EngineDO(), EngineEvent { - data class Result(val settledSession: Session) : SettledSessionResponse() - data class Error(val errorMessage: String) : SettledSessionResponse() - } - - sealed class SessionUpdateNamespacesResponse : EngineDO(), EngineEvent { - data class Result(val topic: Topic, val namespaces: Map) : SessionUpdateNamespacesResponse() - data class Error(val errorMessage: String) : SessionUpdateNamespacesResponse() - } - - data class SessionRejected( - val topic: String, - val reason: String, - ) : EngineDO(), EngineEvent - - data class SessionApproved( - val topic: String, - val peerAppMetaData: AppMetaData?, - val accounts: List, - val namespaces: Map, - ) : EngineDO(), EngineEvent - - data class PairingSettle(val topic: Topic, val appMetaData: AppMetaData?) : EngineDO(), EngineEvent - - data class SessionUpdateNamespaces(val topic: Topic, val namespaces: Map) : EngineDO(), EngineEvent - - data class SessionExtend( - override val topic: Topic, - override val expiry: Expiry, - val pairingTopic: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map?, - val namespaces: Map, - val peerAppMetaData: AppMetaData?, - ) : EngineDO(), Sequence, EngineEvent - - data class Session( - override val topic: Topic, - override val expiry: Expiry, - val pairingTopic: String, - val requiredNamespaces: Map, - val optionalNamespaces: Map?, - val namespaces: Map, - val peerAppMetaData: AppMetaData?, - ) : EngineDO(), Sequence, EngineEvent - - data class Event( - val name: String, - val data: String, - val chainId: String, - ) : EngineDO() - - sealed class JsonRpcResponse : EngineDO() { - abstract val id: Long - - @JsonClass(generateAdapter = true) - data class JsonRpcResult( - override val id: Long, - val jsonrpc: String = "2.0", - val result: String, - ) : JsonRpcResponse() - - @JsonClass(generateAdapter = true) - data class JsonRpcError( - override val id: Long, - val jsonrpc: String = "2.0", - val error: Error, - ) : JsonRpcResponse() - - data class Error( - val code: Int, - val message: String, - ) - } - - data class Request( - val topic: String, - val method: String, - val params: String, - val chainId: String, - val expiry: Expiry? = null - ) : EngineDO() -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/mapper/EngineMapper.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/mapper/EngineMapper.kt deleted file mode 100644 index c45af4b32..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/model/mapper/EngineMapper.kt +++ /dev/null @@ -1,359 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.engine.model.mapper - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SessionProposer -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.Issuer -import com.walletconnect.android.internal.common.signing.cacao.toCAIP222Message -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.exceptions.PeerError -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.common.PayloadParams -import com.walletconnect.sign.common.model.vo.clientsync.common.Requester -import com.walletconnect.sign.common.model.vo.clientsync.common.SessionParticipant -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.ValidationError -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.util.Empty -import java.net.URI -import java.text.SimpleDateFormat -import java.util.Calendar - -@JvmSynthetic -internal fun EngineDO.WalletConnectUri.toAbsoluteString(): String = - "wc:${topic.value}@$version?${getQuery()}&symKey=${symKey.keyAsHex}" - -private fun EngineDO.WalletConnectUri.getQuery(): String { - var query = "relay-protocol=${relay.protocol}" - if (relay.data != null) { - query = "$query&relay-data=${relay.data}" - } - return query -} - -@JvmSynthetic -internal fun SignParams.SessionProposeParams.toEngineDO(topic: Topic): EngineDO.SessionProposal = - EngineDO.SessionProposal( - pairingTopic = topic.value, - name = this.proposer.metadata.name, - description = this.proposer.metadata.description, - url = this.proposer.metadata.url, - icons = this.proposer.metadata.icons.mapNotNull { convertToURI(it) }, - redirect = this.proposer.metadata.redirect?.native ?: String.Empty, - requiredNamespaces = this.requiredNamespaces.toMapOfEngineNamespacesRequired(), - optionalNamespaces = this.optionalNamespaces?.toMapOfEngineNamespacesOptional() ?: emptyMap(), - properties = properties, - proposerPublicKey = this.proposer.publicKey, - relayProtocol = relays.first().protocol, - relayData = relays.first().data - ) - -@JvmSynthetic -internal fun SignParams.SessionProposeParams.toVO(topic: Topic, requestId: Long): ProposalVO = - ProposalVO( - requestId = requestId, - pairingTopic = topic, - name = proposer.metadata.name, - description = proposer.metadata.description, - url = proposer.metadata.url, - icons = proposer.metadata.icons, - redirect = proposer.metadata.redirect?.native ?: String.Empty, - requiredNamespaces = requiredNamespaces, - optionalNamespaces = optionalNamespaces ?: emptyMap(), - properties = properties, - proposerPublicKey = proposer.publicKey, - relayProtocol = relays.first().protocol, - relayData = relays.first().data, - expiry = if (expiryTimestamp != null) Expiry(expiryTimestamp) else null - ) - -@JvmSynthetic -internal fun ProposalVO.toSessionProposeRequest(): WCRequest = - WCRequest( - topic = pairingTopic, - id = requestId, - method = JsonRpcMethod.WC_SESSION_PROPOSE, - params = SignParams.SessionProposeParams( - relays = listOf(RelayProtocolOptions(protocol = relayProtocol, data = relayData)), - proposer = SessionProposer(proposerPublicKey, AppMetaData(name = name, description = description, url = url, icons = icons)), - requiredNamespaces = requiredNamespaces, optionalNamespaces = optionalNamespaces, properties = properties, expiryTimestamp = expiry?.seconds - ), - transportType = TransportType.RELAY - ) - -@JvmSynthetic -internal fun SignParams.SessionRequestParams.toEngineDO( - request: WCRequest, - peerAppMetaData: AppMetaData?, -): EngineDO.SessionRequest = - EngineDO.SessionRequest( - topic = request.topic.value, - chainId = chainId, - peerAppMetaData = peerAppMetaData, - request = EngineDO.SessionRequest.JSONRPCRequest( - id = request.id, - method = this.request.method, - params = this.request.params - ), - if (this.request.expiryTimestamp != null) Expiry(this.request.expiryTimestamp) else null - ) - -@JvmSynthetic -internal fun SignParams.DeleteParams.toEngineDO(topic: Topic): EngineDO.SessionDelete = - EngineDO.SessionDelete(topic.value, message) - -@JvmSynthetic -internal fun SignParams.EventParams.toEngineDO(topic: Topic): EngineDO.SessionEvent = - EngineDO.SessionEvent(topic.value, event.name, event.data.toString(), chainId) - -@JvmSynthetic -internal fun SessionVO.toEngineDO(): EngineDO.Session = - EngineDO.Session( - topic, - expiry, - pairingTopic, - requiredNamespaces.toMapOfEngineNamespacesRequired(), - optionalNamespaces?.toMapOfEngineNamespacesOptional(), - sessionNamespaces.toMapOfEngineNamespacesSession(), - peerAppMetaData - ) - -@JvmSynthetic -internal fun SessionVO.toEngineDOSessionExtend(expiryVO: Expiry): EngineDO.SessionExtend = - EngineDO.SessionExtend( - topic, - expiryVO, - pairingTopic, - requiredNamespaces.toMapOfEngineNamespacesRequired(), - optionalNamespaces?.toMapOfEngineNamespacesOptional(), - sessionNamespaces.toMapOfEngineNamespacesSession(), - selfAppMetaData - ) - -@JvmSynthetic -internal fun SessionVO.toSessionApproved(): EngineDO.SessionApproved = - EngineDO.SessionApproved( - topic = topic.value, - peerAppMetaData = peerAppMetaData, - accounts = sessionNamespaces.flatMap { (_, namespace) -> namespace.accounts }, - namespaces = sessionNamespaces.toMapOfEngineNamespacesSession() - ) - -@JvmSynthetic -internal fun ProposalVO.toSessionSettleParams( - selfParticipant: SessionParticipant, - sessionExpiry: Long, - namespaces: Map, -): SignParams.SessionSettleParams = - SignParams.SessionSettleParams( - relay = RelayProtocolOptions(relayProtocol, relayData), - controller = selfParticipant, - namespaces = namespaces.toMapOfNamespacesVOSession(), - expiry = sessionExpiry, - properties = properties - ) - -@JvmSynthetic -internal fun toSessionProposeParams( - relays: List?, - requiredNamespaces: Map, - optionalNamespaces: Map, - properties: Map?, - selfPublicKey: PublicKey, - appMetaData: AppMetaData, - expiry: Expiry -) = SignParams.SessionProposeParams( - relays = relays ?: listOf(RelayProtocolOptions()), - proposer = SessionProposer(selfPublicKey.keyAsHex, appMetaData), - requiredNamespaces = requiredNamespaces.toNamespacesVORequired(), - optionalNamespaces = optionalNamespaces.toNamespacesVOOptional(), - properties = properties, - expiryTimestamp = expiry.seconds -) - -@JvmSynthetic -internal fun ProposalVO.toEngineDO(): EngineDO.SessionProposal = - EngineDO.SessionProposal( - pairingTopic = pairingTopic.value, - name = name, - description = description, - url = url, - icons = icons.mapNotNull { convertToURI(it) }, - redirect = redirect, - relayData = relayData, - relayProtocol = relayProtocol, - requiredNamespaces = requiredNamespaces.toMapOfEngineNamespacesRequired(), - optionalNamespaces = optionalNamespaces.toMapOfEngineNamespacesOptional(), - proposerPublicKey = proposerPublicKey, - properties = properties - ) - -@JvmSynthetic -internal fun ProposalVO.toExpiredProposal(): EngineDO.ExpiredProposal = EngineDO.ExpiredProposal(pairingTopic.value, proposerPublicKey) - -@JvmSynthetic -internal fun Request.toExpiredSessionRequest() = EngineDO.ExpiredRequest(topic.value, id) - -private fun convertToURI(it: String) = try { - URI(it) -} catch (e: Exception) { - null -} - -@JvmSynthetic -internal fun Map.toNamespacesVORequired(): Map = - this.mapValues { (_, namespace) -> - Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) - } - -@JvmSynthetic -internal fun Map.toNamespacesVOOptional(): Map = - this.mapValues { (_, namespace) -> - Namespace.Proposal(chains = namespace.chains, methods = namespace.methods, events = namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesRequired(): Map = - this.mapValues { (_, namespace) -> - EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesOptional(): Map = - this.mapValues { (_, namespace) -> - EngineDO.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun Map.toMapOfEngineNamespacesSession(): Map = - this.mapValues { (_, namespaceVO) -> - EngineDO.Namespace.Session(namespaceVO.chains, namespaceVO.accounts, namespaceVO.methods, namespaceVO.events) - } - -@JvmSynthetic -internal fun Map.toMapOfNamespacesVOSession(): Map = - this.mapValues { (_, namespace) -> - Namespace.Session(namespace.chains, namespace.accounts, namespace.methods, namespace.events) - } - -@JvmSynthetic -internal fun JsonRpcResponse.JsonRpcResult.toEngineDO(): EngineDO.JsonRpcResponse.JsonRpcResult = - EngineDO.JsonRpcResponse.JsonRpcResult(id = id, result = result.toString()) - -@JvmSynthetic -internal fun JsonRpcResponse.JsonRpcError.toEngineDO(): EngineDO.JsonRpcResponse.JsonRpcError = - EngineDO.JsonRpcResponse.JsonRpcError(id = id, error = EngineDO.JsonRpcResponse.Error(error.code, error.message)) - -@JvmSynthetic -internal fun ProposalVO.toSessionApproveParams(selfPublicKey: PublicKey): CoreSignParams.ApprovalParams = - CoreSignParams.ApprovalParams( - relay = RelayProtocolOptions(relayProtocol, relayData), - responderPublicKey = selfPublicKey.keyAsHex - ) - -@JvmSynthetic -internal fun SignParams.SessionRequestParams.toEngineDO(topic: Topic): EngineDO.Request = - EngineDO.Request(topic.value, request.method, request.params, chainId) - -@JvmSynthetic -internal fun SignParams.EventParams.toEngineDOEvent(): EngineDO.Event = - EngineDO.Event(event.name, event.data.toString(), chainId) - -@JvmSynthetic -internal fun Request.toSessionRequest(peerAppMetaData: AppMetaData?): EngineDO.SessionRequest = - EngineDO.SessionRequest(topic.value, chainId, peerAppMetaData, EngineDO.SessionRequest.JSONRPCRequest(id, method, params), expiry) - - -@JvmSynthetic -internal fun ValidationError.toPeerError() = when (this) { - is ValidationError.UnsupportedNamespaceKey -> PeerError.CAIP25.UnsupportedNamespaceKey(message) - is ValidationError.UnsupportedChains -> PeerError.CAIP25.UnsupportedChains(message) - is ValidationError.InvalidEvent -> PeerError.Invalid.Event(message) - is ValidationError.InvalidExtendRequest -> PeerError.Invalid.ExtendRequest(message) - is ValidationError.InvalidSessionRequest -> PeerError.Invalid.Method(message) - is ValidationError.UnauthorizedEvent -> PeerError.Unauthorized.Event(message) - is ValidationError.UnauthorizedMethod -> PeerError.Unauthorized.Method(message) - is ValidationError.UserRejected -> PeerError.CAIP25.UserRejected(message) - is ValidationError.UserRejectedEvents -> PeerError.CAIP25.UserRejectedEvents(message) - is ValidationError.UserRejectedMethods -> PeerError.CAIP25.UserRejectedMethods(message) - is ValidationError.UserRejectedChains -> PeerError.CAIP25.UserRejectedChains(message) - is ValidationError.InvalidSessionProperties -> PeerError.CAIP25.InvalidSessionPropertiesObject(message) - is ValidationError.EmptyNamespaces -> PeerError.CAIP25.EmptySessionNamespaces(message) -} - -@JvmSynthetic -internal fun VerifyContext.toEngineDO(): EngineDO.VerifyContext = - EngineDO.VerifyContext(id, origin, validation, verifyUrl, isScam) - -@JvmSynthetic -internal fun Requester.toEngineDO(): EngineDO.Participant = - EngineDO.Participant(publicKey, metadata) - -@JvmSynthetic -internal fun EngineDO.Authenticate.toCommon(): PayloadParams = - PayloadParams( - domain = domain, - aud = aud, - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - chains = chains, - type = type ?: CacaoType.EIP4361.header, - version = "1", - iat = SimpleDateFormat(Cacao.Payload.ISO_8601_PATTERN).format(Calendar.getInstance().time) - ) - -@JvmSynthetic -internal fun PayloadParams.toEngineDO(): EngineDO.PayloadParams = - EngineDO.PayloadParams( - domain = domain, - aud = aud, - version = "1", - nonce = nonce, - nbf = nbf, - exp = exp, - statement = statement, - requestId = requestId, - resources = resources, - chains = chains, - type = type, - iat = iat - ) - -@JvmSynthetic -internal fun EngineDO.PayloadParams.toCacaoPayload(iss: Issuer): Cacao.Payload = - Cacao.Payload( - iss.value, - domain = domain, - aud = aud, - version = version, - nonce = nonce, - nbf = nbf, - exp = exp, - iat = iat, - statement = statement, - requestId = requestId, - resources = resources - ) - -@JvmSynthetic -internal fun EngineDO.PayloadParams.toCAIP222Message(iss: Issuer, chainName: String): String = - this.toCacaoPayload(iss).toCAIP222Message(chainName) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt deleted file mode 100644 index f04c28eca..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DecryptSignMessageUseCase.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.codec.Codec -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.sync.ClientJsonRpc -import com.walletconnect.android.internal.common.model.type.ClientParams -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.push_messages.PushMessagesRepository -import com.walletconnect.android.push.notifications.DecryptMessageUseCaseInterface -import com.walletconnect.android.utils.toClient -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.exceptions.InvalidSignParamsType -import com.walletconnect.sign.common.model.vo.clientsync.common.PayloadParams -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import org.bouncycastle.util.encoders.Base64 - -internal class DecryptSignMessageUseCase( - private val codec: Codec, - private val serializer: JsonRpcSerializer, - private val metadataRepository: MetadataStorageRepositoryInterface, - private val pushMessageStorage: PushMessagesRepository -) : DecryptMessageUseCaseInterface { - override suspend fun decryptNotification(topic: String, message: String, onSuccess: (Core.Model.Message) -> Unit, onFailure: (Throwable) -> Unit) { - try { - if (!pushMessageStorage.doesPushMessageExist(sha256(message.toByteArray()))) { - val decryptedMessageString = codec.decrypt(Topic(topic), Base64.decode(message)) - val clientJsonRpc: ClientJsonRpc = serializer.tryDeserialize(decryptedMessageString) ?: return onFailure(InvalidSignParamsType()) - val params: ClientParams = serializer.deserialize(clientJsonRpc.method, decryptedMessageString) ?: return onFailure(InvalidSignParamsType()) - val metadata: AppMetaData = metadataRepository.getByTopicAndType(Topic(topic), AppMetaDataType.PEER) ?: return onFailure(InvalidSignParamsType()) - - when (params) { - is SignParams.SessionProposeParams -> onSuccess(params.toCore(clientJsonRpc.id, topic)) - is SignParams.SessionRequestParams -> onSuccess(params.toCore(clientJsonRpc.id, topic, metadata)) - is SignParams.SessionAuthenticateParams -> onSuccess(params.toCore(clientJsonRpc.id, topic, metadata)) - else -> onFailure(InvalidSignParamsType()) - } - } - } catch (e: Exception) { - onFailure(e) - } - } - - private companion object { - fun SignParams.SessionProposeParams.toCore(id: Long, topic: String): Core.Model.Message.SessionProposal = - Core.Model.Message.SessionProposal( - id, - topic, - proposer.metadata.name, - proposer.metadata.description, - proposer.metadata.url, - proposer.metadata.icons, - proposer.metadata.redirect?.native ?: "", - requiredNamespaces.toCore(), - (optionalNamespaces ?: emptyMap()).toCore(), - properties, - proposer.publicKey, - relays.first().protocol, - relays.first().data - ) - - fun SignParams.SessionRequestParams.toCore(id: Long, topic: String, metaData: AppMetaData): Core.Model.Message.SessionRequest = - Core.Model.Message.SessionRequest( - topic, - chainId, - metaData.toClient(), - Core.Model.Message.SessionRequest.JSONRPCRequest(id, request.method, request.params) - ) - - fun SignParams.SessionAuthenticateParams.toCore(id: Long, topic: String, metaData: AppMetaData): Core.Model.Message.SessionAuthenticate = - Core.Model.Message.SessionAuthenticate( - id, - topic, - metaData.toClient(), - authPayload.toClient(), - expiryTimestamp - ) - - fun PayloadParams.toClient(): Core.Model.Message.SessionAuthenticate.PayloadParams { - return with(this) { - Core.Model.Message.SessionAuthenticate.PayloadParams(chains, domain, nonce, aud, type, nbf, exp, statement, requestId, resources, iat) - } - } - - fun Map.toCore(): Map = - mapValues { (_, namespace) -> Core.Model.Namespace.Proposal(namespace.chains, namespace.methods, namespace.events) } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCase.kt deleted file mode 100644 index f73590a1b..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCase.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.exception.Reason -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class DisconnectSessionUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger, -) : DisconnectSessionUseCaseInterface { - override suspend fun disconnect(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - if (!sessionStorageRepository.isSessionValid(Topic(topic))) { - logger.error("Sending session disconnect error: invalid session $topic") - return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) - } - - val deleteParams = SignParams.DeleteParams(Reason.UserDisconnected.code, Reason.UserDisconnected.message) - val sessionDelete = SignRpc.SessionDelete(params = deleteParams) - val irnParams = IrnParams(Tags.SESSION_DELETE, Ttl(dayInSeconds)) - - logger.log("Sending session disconnect on topic: $topic") - jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, sessionDelete, - onSuccess = { - logger.log("Disconnect sent successfully on topic: $topic") - sessionStorageRepository.deleteSession(Topic(topic)) - jsonRpcInteractor.unsubscribe(Topic(topic)) - onSuccess() - }, - onFailure = { error -> - logger.error("Sending session disconnect error: $error on topic: $topic") - onFailure(error) - } - ) - } -} - -internal interface DisconnectSessionUseCaseInterface { - suspend fun disconnect(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCase.kt deleted file mode 100644 index 3c3bce582..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCase.kt +++ /dev/null @@ -1,84 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidEventException -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.common.exceptions.UNAUTHORIZED_EMIT_MESSAGE -import com.walletconnect.sign.common.exceptions.UnauthorizedEventException -import com.walletconnect.sign.common.exceptions.UnauthorizedPeerException -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionEventVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.util.generateId -import kotlinx.coroutines.supervisorScope - -internal class EmitEventUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger, -) : EmitEventUseCaseInterface { - - override suspend fun emit(topic: String, event: EngineDO.Event, id: Long?, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - runCatching { validate(topic, event) }.fold( - onSuccess = { - val eventParams = SignParams.EventParams(SessionEventVO(event.name, event.data), event.chainId) - val sessionEvent = SignRpc.SessionEvent(id = id ?: generateId(), params = eventParams) - val irnParams = IrnParams(Tags.SESSION_EVENT, Ttl(fiveMinutesInSeconds), true) - - logger.log("Emitting event on topic: $topic") - jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, sessionEvent, - onSuccess = { - logger.log("Event sent successfully, on topic: $topic") - onSuccess() - }, - onFailure = { error -> - logger.error("Sending event error: $error, on topic: $topic") - onFailure(error) - } - ) - }, - onFailure = { error -> - logger.error("Sending event error: $error, on topic: $topic") - onFailure(error) - } - ) - } - - private fun validate(topic: String, event: EngineDO.Event) { - if (!sessionStorageRepository.isSessionValid(Topic(topic))) { - logger.error("Emit - cannot find sequence for topic: $topic") - throw CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic") - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) - if (!session.isSelfController) { - logger.error("Emit - unauthorized peer: $topic") - throw UnauthorizedPeerException(UNAUTHORIZED_EMIT_MESSAGE) - } - - SignValidator.validateEvent(event) { error -> - logger.error("Emit - invalid event: $topic") - throw InvalidEventException(error.message) - } - - val namespaces = session.sessionNamespaces - SignValidator.validateChainIdWithEventAuthorisation(event.chainId, event.name, namespaces) { error -> - logger.error("Emit - unauthorized event: $topic") - throw UnauthorizedEventException(error.message) - } - } -} - -internal interface EmitEventUseCaseInterface { - suspend fun emit(topic: String, event: EngineDO.Event, id: Long? = null, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCase.kt deleted file mode 100644 index bc5f2b5fa..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCase.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.internal.utils.weekInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.common.exceptions.NotSettledSessionException -import com.walletconnect.sign.common.exceptions.SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class ExtendSessionUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger, -) : ExtendSessionUseCaseInterface { - - override suspend fun extend(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - if (!sessionStorageRepository.isSessionValid(Topic(topic))) { - return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) - if (!session.isAcknowledged) { - logger.error("Sending session extend error: not acknowledged session on topic: $topic") - return@supervisorScope onFailure(NotSettledSessionException("$SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE$topic")) - } - - val newExpiration = session.expiry.seconds + weekInSeconds - sessionStorageRepository.extendSession(Topic(topic), newExpiration) - val sessionExtend = SignRpc.SessionExtend(params = SignParams.ExtendParams(newExpiration)) - val irnParams = IrnParams(Tags.SESSION_EXTEND, Ttl(dayInSeconds)) - - logger.log("Sending session extend on topic: $topic") - jsonRpcInteractor.publishJsonRpcRequest( - Topic(topic), irnParams, sessionExtend, - onSuccess = { - logger.log("Session extend sent successfully on topic: $topic") - onSuccess() - }, - onFailure = { error -> - logger.error("Sending session extend error: $error on topic: $topic") - onFailure(error) - }) - } -} - -internal interface ExtendSessionUseCaseInterface { - suspend fun extend(topic: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt deleted file mode 100644 index 87686af16..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetListOfVerifyContextsUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO - -internal class GetListOfVerifyContextsUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetListOfVerifyContextsUseCaseInterface { - override suspend fun getListOfVerifyContexts(): List = verifyContextStorageRepository.getAll().map { verifyContext -> verifyContext.toEngineDO() } -} - -internal interface GetListOfVerifyContextsUseCaseInterface { - suspend fun getListOfVerifyContexts(): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt deleted file mode 100644 index 139592f8c..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCaps.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.sign.common.validator.SignValidator - -class GetNamespacesFromReCaps { - operator fun invoke(chains: List, methods: List): Map { - if (!chains.all { chain -> CoreValidator.isChainIdCAIP2Compliant(chain) }) throw Exception("Chains are not CAIP-2 compliant") - if (!chains.all { chain -> SignValidator.getNamespaceKeyFromChainId(chain) == EIP155 }) throw Exception("Only eip155 (EVM) is supported") - val namespace = SignValidator.getNamespaceKeyFromChainId(chains.first()) - return mapOf(namespace to Namespace.Proposal(events = listOf("chainChanged", "accountsChanged"), methods = methods, chains = chains)) - } - - companion object { - private const val EIP155 = "eip155" - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingsUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingsUseCase.kt deleted file mode 100644 index b4554760c..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingsUseCase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.pairing.client.PairingInterface -import com.walletconnect.android.pairing.model.mapper.toPairing -import com.walletconnect.sign.engine.model.EngineDO -import kotlinx.coroutines.supervisorScope - -internal class GetPairingsUseCase(private val pairingInterface: PairingInterface) : GetPairingsUseCaseInterface { - - override suspend fun getListOfSettledPairings(): List = supervisorScope { - return@supervisorScope pairingInterface.getPairings().map { pairing -> - val mappedPairing = pairing.toPairing() - EngineDO.PairingSettle(mappedPairing.topic, mappedPairing.peerAppMetaData) - } - } -} - -internal interface GetPairingsUseCaseInterface { - suspend fun getListOfSettledPairings(): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt deleted file mode 100644 index ec5b00c33..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPendingAuthenticateRequestUseCase.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.json_rpc.model.toRequest - -internal class GetPendingAuthenticateRequestUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) : GetPendingAuthenticateRequestUseCaseInterface { - override suspend fun getPendingAuthenticateRequests(): List> { - return jsonRpcHistory.getListOfPendingRecords() - .filter { record -> record.method == JsonRpcMethod.WC_SESSION_AUTHENTICATE } - .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } - } -} - -internal interface GetPendingAuthenticateRequestUseCaseInterface { - suspend fun getPendingAuthenticateRequests(): List> -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt deleted file mode 100644 index e6f570e53..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionProposalsUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class GetSessionProposalsUseCase(private val proposalStorageRepository: ProposalStorageRepository) : GetSessionProposalsUseCaseInterface { - override suspend fun getSessionProposals(): List = - supervisorScope { - proposalStorageRepository.getProposals().filter { proposal -> proposal.expiry?.let { !it.isExpired() } ?: true }.map(ProposalVO::toEngineDO) - } -} - -internal interface GetSessionProposalsUseCaseInterface { - suspend fun getSessionProposals(): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionsUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionsUseCase.kt deleted file mode 100644 index 7f439f3e3..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetSessionsUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.isSequenceValid -import kotlinx.coroutines.supervisorScope - -internal class GetSessionsUseCase( - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val selfAppMetaData: AppMetaData -) : GetSessionsUseCaseInterface { - - override suspend fun getListOfSettledSessions(): List = supervisorScope { - return@supervisorScope sessionStorageRepository.getListOfSessionVOsWithoutMetadata() - .filter { session -> session.isAcknowledged && session.expiry.isSequenceValid() } - .map { session -> session.copy(selfAppMetaData = selfAppMetaData, peerAppMetaData = metadataStorageRepository.getByTopicAndType(session.topic, AppMetaDataType.PEER)) } - .map { session -> session.toEngineDO() } - } -} - -internal interface GetSessionsUseCaseInterface { - suspend fun getListOfSettledSessions(): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt deleted file mode 100644 index cfb7b0d66..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/GetVerifyContextByIdUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO - -internal class GetVerifyContextByIdUseCase(private val verifyContextStorageRepository: VerifyContextStorageRepository) : GetVerifyContextByIdUseCaseInterface { - override suspend fun getVerifyContext(id: Long): EngineDO.VerifyContext? = verifyContextStorageRepository.get(id)?.toEngineDO() -} - -internal interface GetVerifyContextByIdUseCaseInterface { - suspend fun getVerifyContext(id: Long): EngineDO.VerifyContext? -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCase.kt deleted file mode 100644 index 3982b6468..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCase.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.PROPOSAL_EXPIRY -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.common.exceptions.InvalidPropertiesException -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toNamespacesVOOptional -import com.walletconnect.sign.engine.model.mapper.toNamespacesVORequired -import com.walletconnect.sign.engine.model.mapper.toSessionProposeParams -import com.walletconnect.sign.engine.model.mapper.toVO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class ProposeSessionUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val crypto: KeyManagementRepository, - private val proposalStorageRepository: ProposalStorageRepository, - private val selfAppMetaData: AppMetaData, - private val logger: Logger -) : ProposeSessionUseCaseInterface { - - override suspend fun proposeSession( - requiredNamespaces: Map?, - optionalNamespaces: Map?, - properties: Map?, - pairing: Pairing, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) = supervisorScope { - val relay = RelayProtocolOptions(pairing.relayProtocol, pairing.relayData) - - runCatching { validate(requiredNamespaces, optionalNamespaces, properties) }.fold( - onSuccess = { - val expiry = Expiry(PROPOSAL_EXPIRY) - val selfPublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() - val sessionProposal: SignParams.SessionProposeParams = - toSessionProposeParams( - listOf(relay), - requiredNamespaces ?: emptyMap(), - optionalNamespaces ?: emptyMap(), - properties, selfPublicKey, selfAppMetaData, expiry - ) - val request = SignRpc.SessionPropose(params = sessionProposal) - proposalStorageRepository.insertProposal(sessionProposal.toVO(pairing.topic, request.id)) - val irnParams = IrnParams(Tags.SESSION_PROPOSE, Ttl(fiveMinutesInSeconds), true) - jsonRpcInteractor.subscribe(pairing.topic) { error -> onFailure(error) } - - logger.log("Sending proposal on topic: ${pairing.topic.value}") - jsonRpcInteractor.publishJsonRpcRequest(pairing.topic, irnParams, request, - onSuccess = { - logger.log("Session proposal sent successfully, topic: ${pairing.topic}") - onSuccess() - }, - onFailure = { error -> - logger.error("Failed to send a session proposal: $error") - onFailure(error) - } - ) - }, - onFailure = { error -> - logger.error("Failed to validate session proposal: $error") - onFailure(error) - } - ) - } - - private fun validate( - requiredNamespaces: Map?, - optionalNamespaces: Map?, - properties: Map? - ) { - requiredNamespaces?.let { namespaces -> - SignValidator.validateProposalNamespaces(namespaces.toNamespacesVORequired()) { error -> - logger.error("Failed to send a session proposal - required namespaces error: $error") - throw InvalidNamespaceException(error.message) - } - } - - optionalNamespaces?.let { namespaces -> - SignValidator.validateProposalNamespaces(namespaces.toNamespacesVOOptional()) { error -> - logger.error("Failed to send a session proposal - optional namespaces error: $error") - throw InvalidNamespaceException(error.message) - } - } - - properties?.let { - SignValidator.validateProperties(properties) { error -> - logger.error("Failed to send a session proposal - session properties error: $error") - throw InvalidPropertiesException(error.message) - } - } - } -} - -internal interface ProposeSessionUseCaseInterface { - suspend fun proposeSession( - requiredNamespaces: Map?, - optionalNamespaces: Map?, - properties: Map?, - pairing: Pairing, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt deleted file mode 100644 index 039dd8f2e..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCase.kt +++ /dev/null @@ -1,111 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.RequestExpiredException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Participants -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.MissingSessionAuthenticateRequest -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class RejectSessionAuthenticateUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val getPendingSessionAuthenticateRequest: GetPendingSessionAuthenticateRequest, - private val crypto: KeyManagementRepository, - private val verifyContextStorageRepository: VerifyContextStorageRepository, - private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, - private val insertEventUseCase: InsertEventUseCase, - private val clientId: String, - private val logger: Logger -) : RejectSessionAuthenticateUseCaseInterface { - override suspend fun rejectSessionAuthenticate(id: Long, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - val jsonRpcHistoryEntry = getPendingSessionAuthenticateRequest(id) - if (jsonRpcHistoryEntry == null) { - logger.error(MissingSessionAuthenticateRequest().message) - onFailure(MissingSessionAuthenticateRequest()) - return@supervisorScope - } - - jsonRpcHistoryEntry.expiry?.let { - if (it.isExpired()) { - logger.error("Session Authenticate Request Expired: ${jsonRpcHistoryEntry.topic}, id: ${jsonRpcHistoryEntry.id}") - throw RequestExpiredException("This request has expired, id: ${jsonRpcHistoryEntry.id}") - } - } - - //todo: handle error codes - val response = JsonRpcResponse.JsonRpcError(id, error = JsonRpcResponse.Error(12001, reason)) - val sessionAuthenticateParams: SignParams.SessionAuthenticateParams = jsonRpcHistoryEntry.params - val receiverMetadata: AppMetaData = sessionAuthenticateParams.requester.metadata - val receiverPublicKey = PublicKey(sessionAuthenticateParams.requester.publicKey) - val senderPublicKey: PublicKey = crypto.generateAndStoreX25519KeyPair() - val symmetricKey: SymmetricKey = crypto.generateSymmetricKeyFromKeyAgreement(senderPublicKey, receiverPublicKey) - val responseTopic: Topic = crypto.getTopicFromKey(receiverPublicKey) - crypto.setKey(symmetricKey, responseTopic.value) - - if (jsonRpcHistoryEntry.transportType == TransportType.LINK_MODE && receiverMetadata.redirect?.linkMode == true) { - if (receiverMetadata.redirect?.universal.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) - try { - linkModeJsonRpcInteractor.triggerResponse( - responseTopic, - response, - receiverMetadata.redirect?.universal!!, - Participants(senderPublicKey, receiverPublicKey), - EnvelopeType.ONE - ) - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_AUTHENTICATE_LINK_MODE_RESPONSE_REJECT.id.toString(), - Properties(clientId = clientId, correlationId = id, direction = Direction.SENT.state) - ) - ) - } catch (e: Exception) { - onFailure(e) - } - } else { - val irnParams = IrnParams(Tags.SESSION_AUTHENTICATE_RESPONSE_REJECT, Ttl(dayInSeconds), false) - logger.log("Sending Session Authenticate Reject on topic: $responseTopic") - jsonRpcInteractor.publishJsonRpcResponse( - responseTopic, irnParams, response, envelopeType = EnvelopeType.ONE, participants = Participants(senderPublicKey, receiverPublicKey), - onSuccess = { - logger.log("Session Authenticate Reject Responded on topic: $responseTopic") - scope.launch { supervisorScope { verifyContextStorageRepository.delete(id) } } - onSuccess() - }, - onFailure = { error -> - logger.error("Session Authenticate Error Responded on topic: $responseTopic") - scope.launch { supervisorScope { verifyContextStorageRepository.delete(id) } } - onFailure(error) - } - ) - } - } -} - -internal interface RejectSessionAuthenticateUseCaseInterface { - suspend fun rejectSessionAuthenticate(id: Long, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionUseCase.kt deleted file mode 100644 index 8517eb6dd..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionUseCase.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.PeerError -import com.walletconnect.sign.common.exceptions.SessionProposalExpiredException -import com.walletconnect.sign.engine.model.mapper.toSessionProposeRequest -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class RejectSessionUseCase( - private val verifyContextStorageRepository: VerifyContextStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val proposalStorageRepository: ProposalStorageRepository, - private val logger: Logger -) : RejectSessionUseCaseInterface { - - override suspend fun reject(proposerPublicKey: String, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - val proposal = proposalStorageRepository.getProposalByKey(proposerPublicKey) - proposal.expiry?.let { - if (it.isExpired()) { - logger.error("Proposal expired on reject, topic: ${proposal.pairingTopic.value}, id: ${proposal.requestId}") - throw SessionProposalExpiredException("Session proposal expired") - } - } - - logger.log("Sending session rejection, topic: ${proposal.pairingTopic.value}") - jsonRpcInteractor.respondWithError( - proposal.toSessionProposeRequest(), - PeerError.EIP1193.UserRejectedRequest(reason), - IrnParams(Tags.SESSION_PROPOSE_RESPONSE_REJECT, Ttl(fiveMinutesInSeconds)), - onSuccess = { - logger.log("Session rejection sent successfully, topic: ${proposal.pairingTopic.value}") - scope.launch { - proposalStorageRepository.deleteProposal(proposerPublicKey) - verifyContextStorageRepository.delete(proposal.requestId) - } - onSuccess() - }, - onFailure = { error -> - logger.error("Session rejection sent failure, topic: ${proposal.pairingTopic.value}. Error: $error") - onFailure(error) - }) - } -} - -internal interface RejectSessionUseCaseInterface { - suspend fun reject(proposerPublicKey: String, reason: String, onSuccess: () -> Unit, onFailure: (Throwable) -> Unit = {}) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt deleted file mode 100644 index 51c29b089..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCase.kt +++ /dev/null @@ -1,135 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.exception.RequestExpiredException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.engine.sessionRequestEventsQueue -import com.walletconnect.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class RespondSessionRequestUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val getPendingJsonRpcHistoryEntryByIdUseCase: GetPendingJsonRpcHistoryEntryByIdUseCase, - private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, - private val logger: Logger, - private val verifyContextStorageRepository: VerifyContextStorageRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val insertEventUseCase: InsertEventUseCase, - private val clientId: String, -) : RespondSessionRequestUseCaseInterface { - private val _events: MutableSharedFlow = MutableSharedFlow() - override val events: SharedFlow = _events.asSharedFlow() - override suspend fun respondSessionRequest( - topic: String, - jsonRpcResponse: JsonRpcResponse, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) = supervisorScope { - val topicWrapper = Topic(topic) - if (!sessionStorageRepository.isSessionValid(topicWrapper)) { - logger.error("Request response - invalid session: $topic, id: ${jsonRpcResponse.id}") - return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic")) - } - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(topicWrapper) - .run { - val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) - this.copy(peerAppMetaData = peerAppMetaData) - } - - if (getPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcResponse.id) == null) { - logger.error("Request doesn't exist: $topic, id: ${jsonRpcResponse.id}") - throw RequestExpiredException("This request has expired, id: ${jsonRpcResponse.id}") - } - getPendingJsonRpcHistoryEntryByIdUseCase(jsonRpcResponse.id)?.params?.request?.expiryTimestamp?.let { - if (Expiry(it).isExpired()) { - logger.error("Request Expired: $topic, id: ${jsonRpcResponse.id}") - throw RequestExpiredException("This request has expired, id: ${jsonRpcResponse.id}") - } - } - - if (session.transportType == TransportType.LINK_MODE && session.peerLinkMode == true) { - if (session.peerAppLink.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) - try { - removePendingSessionRequestAndEmit(jsonRpcResponse.id) - linkModeJsonRpcInteractor.triggerResponse(Topic(topic), jsonRpcResponse, session.peerAppLink) - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_REQUEST_LINK_MODE_RESPONSE.id.toString(), - Properties(correlationId = jsonRpcResponse.id, clientId = clientId, direction = Direction.SENT.state) - ) - ) - } catch (e: Exception) { - onFailure(e) - } - } else { - val irnParams = IrnParams(Tags.SESSION_REQUEST_RESPONSE, Ttl(fiveMinutesInSeconds)) - logger.log("Sending session request response on topic: $topic, id: ${jsonRpcResponse.id}") - jsonRpcInteractor.publishJsonRpcResponse(topic = Topic(topic), params = irnParams, response = jsonRpcResponse, - onSuccess = { - onSuccess() - logger.log("Session request response sent successfully on topic: $topic, id: ${jsonRpcResponse.id}") - scope.launch { - supervisorScope { - removePendingSessionRequestAndEmit(jsonRpcResponse.id) - } - } - }, - onFailure = { error -> - logger.error("Sending session response error: $error, id: ${jsonRpcResponse.id}") - onFailure(error) - } - ) - } - } - - private suspend fun removePendingSessionRequestAndEmit(id: Long) { - verifyContextStorageRepository.delete(id) - sessionRequestEventsQueue.find { pendingRequestEvent -> pendingRequestEvent.request.request.id == id }?.let { event -> - sessionRequestEventsQueue.remove(event) - } - if (sessionRequestEventsQueue.isNotEmpty()) { - sessionRequestEventsQueue.find { event -> if (event.request.expiry != null) !event.request.expiry.isExpired() else true }?.let { event -> - _events.emit(event) - } - } - } -} - -internal interface RespondSessionRequestUseCaseInterface { - val events: SharedFlow - suspend fun respondSessionRequest( - topic: String, - jsonRpcResponse: JsonRpcResponse, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCase.kt deleted file mode 100644 index 41d8244c4..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCase.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.exception.InvalidExpiryException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.CoreValidator -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidRequestException -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.common.exceptions.UnauthorizedMethodException -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.withTimeout -import java.util.concurrent.TimeUnit - -internal class SessionRequestUseCase( - private val sessionStorageRepository: SessionStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val insertEventUseCase: InsertEventUseCase, - private val clientId: String, - private val logger: Logger, -) : SessionRequestUseCaseInterface { - private val _errors: MutableSharedFlow = MutableSharedFlow() - override val errors: SharedFlow = _errors.asSharedFlow() - - override suspend fun sessionRequest(request: EngineDO.Request, onSuccess: (Long) -> Unit, onFailure: (Throwable) -> Unit) = supervisorScope { - if (!sessionStorageRepository.isSessionValid(Topic(request.topic))) { - return@supervisorScope onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE${request.topic}")) - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(request.topic)) - .run { - val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) - this.copy(peerAppMetaData = peerAppMetaData) - } - - val nowInSeconds = currentTimeInSeconds - if (!CoreValidator.isExpiryWithinBounds(request.expiry)) { - logger.error("Sending session request error: expiry not within bounds") - return@supervisorScope onFailure(InvalidExpiryException()) - } - val expiry = request.expiry ?: Expiry(currentTimeInSeconds + fiveMinutesInSeconds) - SignValidator.validateSessionRequest(request) { error -> - logger.error("Sending session request error: invalid session request, ${error.message}") - return@supervisorScope onFailure(InvalidRequestException(error.message)) - } - - val namespaces: Map = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(request.topic)).sessionNamespaces - SignValidator.validateChainIdWithMethodAuthorisation(request.chainId, request.method, namespaces) { error -> - logger.error("Sending session request error: unauthorized method, ${error.message}") - return@supervisorScope onFailure(UnauthorizedMethodException(error.message)) - } - - val params = SignParams.SessionRequestParams(SessionRequestVO(request.method, request.params, expiry.seconds), request.chainId) - val sessionPayload = SignRpc.SessionRequest(params = params) - - if (session.transportType == TransportType.LINK_MODE && session.peerLinkMode == true) { - if (session.peerAppLink.isNullOrEmpty()) return@supervisorScope onFailure(IllegalStateException("App link is missing")) - try { - linkModeJsonRpcInteractor.triggerRequest(sessionPayload, Topic(request.topic), session.peerAppLink) - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_REQUEST_LINK_MODE.id.toString(), - Properties(correlationId = sessionPayload.id, clientId = clientId, direction = Direction.SENT.state) - ) - ) - } catch (e: Exception) { - onFailure(e) - } - } else { - val irnParamsTtl = expiry.run { - val defaultTtl = fiveMinutesInSeconds - val extractedTtl = seconds - nowInSeconds - val newTtl = extractedTtl.takeIf { extractedTtl >= defaultTtl } ?: defaultTtl - - Ttl(newTtl) - } - val irnParams = IrnParams(Tags.SESSION_REQUEST, irnParamsTtl, true) - val requestTtlInSeconds = expiry.run { seconds - nowInSeconds } - - logger.log("Sending session request on topic: ${request.topic}}") - jsonRpcInteractor.publishJsonRpcRequest(Topic(request.topic), irnParams, sessionPayload, - onSuccess = { - logger.log("Session request sent successfully on topic: ${request.topic}") - onSuccess(sessionPayload.id) - scope.launch { - try { - withTimeout(TimeUnit.SECONDS.toMillis(requestTtlInSeconds)) { - collectResponse(sessionPayload.id) { cancel() } - } - } catch (e: TimeoutCancellationException) { - _errors.emit(SDKError(e)) - } - } - }, - onFailure = { error -> - logger.error("Sending session request error: $error") - onFailure(error) - } - ) - } - } - - private suspend fun collectResponse(id: Long, onResponse: (Result) -> Unit = {}) { - jsonRpcInteractor.peerResponse - .filter { response -> response.response.id == id } - .collect { response -> - when (val result = response.response) { - is JsonRpcResponse.JsonRpcResult -> onResponse(Result.success(result)) - is JsonRpcResponse.JsonRpcError -> onResponse(Result.failure(Throwable(result.errorMessage))) - } - } - } -} - -internal interface SessionRequestUseCaseInterface { - val errors: SharedFlow - suspend fun sessionRequest(request: EngineDO.Request, onSuccess: (Long) -> Unit, onFailure: (Throwable) -> Unit) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCase.kt deleted file mode 100644 index 6d3529b57..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCase.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.exception.GenericException -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.common.exceptions.NO_SEQUENCE_FOR_TOPIC_MESSAGE -import com.walletconnect.sign.common.exceptions.NotSettledSessionException -import com.walletconnect.sign.common.exceptions.SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE -import com.walletconnect.sign.common.exceptions.UNAUTHORIZED_UPDATE_MESSAGE -import com.walletconnect.sign.common.exceptions.UnauthorizedPeerException -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toMapOfNamespacesVOSession -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class SessionUpdateUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger, -) : SessionUpdateUseCaseInterface { - - override suspend fun sessionUpdate( - topic: String, - namespaces: Map, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) = supervisorScope { - runCatching { validate(topic, namespaces) }.fold( - onSuccess = { - val params = SignParams.UpdateNamespacesParams(namespaces.toMapOfNamespacesVOSession()) - val sessionUpdate = SignRpc.SessionUpdate(params = params) - val irnParams = IrnParams(Tags.SESSION_UPDATE, Ttl(dayInSeconds)) - - try { - logger.log("Sending session update on topic: $topic") - sessionStorageRepository.insertTempNamespaces(topic, namespaces.toMapOfNamespacesVOSession(), sessionUpdate.id) - jsonRpcInteractor.publishJsonRpcRequest( - Topic(topic), irnParams, sessionUpdate, - onSuccess = { - logger.log("Update sent successfully, topic: $topic") - onSuccess() - }, - onFailure = { error -> - logger.error("Sending session update error: $error, topic: $topic") - sessionStorageRepository.deleteTempNamespacesByRequestId(sessionUpdate.id) - onFailure(error) - }) - } catch (e: Exception) { - logger.error("Error updating namespaces: $e") - onFailure(GenericException("Error updating namespaces: $e")) - } - }, - onFailure = { - logger.error("Error updating namespaces: $it") - onFailure(it) - } - ) - } - - private fun validate(topic: String, namespaces: Map) { - if (!sessionStorageRepository.isSessionValid(Topic(topic))) { - logger.error("Sending session update error: cannot find sequence for topic: $topic") - throw CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic") - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(Topic(topic)) - - if (!session.isSelfController) { - logger.error("Sending session update error: unauthorized peer") - throw UnauthorizedPeerException(UNAUTHORIZED_UPDATE_MESSAGE) - } - - if (!session.isAcknowledged) { - logger.error("Sending session update error: session is not acknowledged") - throw NotSettledSessionException("$SESSION_IS_NOT_ACKNOWLEDGED_MESSAGE$topic") - } - - SignValidator.validateSessionNamespace(namespaces.toMapOfNamespacesVOSession(), session.requiredNamespaces) { error -> - logger.error("Sending session update error: invalid namespaces $error") - throw InvalidNamespaceException(error.message) - } - } -} - -internal interface SessionUpdateUseCaseInterface { - suspend fun sessionUpdate( - topic: String, - namespaces: Map, - onSuccess: () -> Unit, - onFailure: (Throwable) -> Unit, - ) -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnPingUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnPingUseCase.kt deleted file mode 100644 index 944ba6ac0..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnPingUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.thirtySeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import kotlinx.coroutines.supervisorScope - -internal class OnPingUseCase(private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, private val logger: Logger) { - - suspend operator fun invoke(request: WCRequest) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_PING_RESPONSE, Ttl(thirtySeconds)) - logger.log("Session ping received on topic: ${request.topic}") - jsonRpcInteractor.respondWithSuccess(request, irnParams) - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt deleted file mode 100644 index b3209f9ae..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionAuthenticateUseCase.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.exception.Invalid -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class OnSessionAuthenticateUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, - private val pairingController: PairingControllerInterface, - private val insertTelemetryEventUseCase: InsertTelemetryEventUseCase, - private val insertEventUseCase: InsertEventUseCase, - private val clientId: String, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, authenticateSessionParams: SignParams.SessionAuthenticateParams) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_AUTHENTICATE_RESPONSE_AUTO_REJECT, Ttl(dayInSeconds)) - logger.log("Received session authenticate: ${request.topic}") - try { - if (Expiry(authenticateSessionParams.expiryTimestamp).isExpired()) { - logger.log("Received session authenticate - expiry error: ${request.topic}") - .also { insertTelemetryEventUseCase(Props(type = EventType.Error.AUTHENTICATED_SESSION_EXPIRED, properties = Properties(topic = request.topic.value))) } - jsonRpcInteractor.respondWithError(request, Invalid.RequestExpired, irnParams) - _events.emit(SDKError(Throwable("Received session authenticate - expiry error: ${request.topic}"))) - return@supervisorScope - } - - val url = authenticateSessionParams.metadataUrl - pairingController.setRequestReceived(Core.Params.RequestReceived(request.topic.value)) - if (request.transportType == TransportType.LINK_MODE) { - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_AUTHENTICATE_LINK_MODE.id.toString(), - Properties(correlationId = request.id, clientId = clientId, direction = Direction.RECEIVED.state) - ) - ) - } - resolveAttestationIdUseCase(request, url, linkMode = authenticateSessionParams.linkMode, appLink = authenticateSessionParams.appLink) { verifyContext -> - emitSessionAuthenticate(request, authenticateSessionParams, verifyContext) - } - } catch (e: Exception) { - logger.log("Received session authenticate - cannot handle request: ${request.topic}") - jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot handle a auth request: ${e.message}, topic: ${request.topic}"), irnParams) - _events.emit(SDKError(e)) - } - } - - private fun emitSessionAuthenticate( - request: WCRequest, - authenticateSessionParams: SignParams.SessionAuthenticateParams, - verifyContext: VerifyContext - ) { - scope.launch { - logger.log("Received session authenticate - emitting: ${request.topic}") - _events.emit( - EngineDO.SessionAuthenticateEvent( - request.id, - request.topic.value, - authenticateSessionParams.authPayload.toEngineDO(), - authenticateSessionParams.requester.toEngineDO(), - authenticateSessionParams.expiryTimestamp, - verifyContext.toEngineDO() - ) - ) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt deleted file mode 100644 index 787845c91..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionDeleteUseCase.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.type.Sequences -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionDeleteUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val crypto: KeyManagementRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: SignParams.DeleteParams) = supervisorScope { - logger.log("Session delete received on topic: ${request.topic}") - val irnParams = IrnParams(Tags.SESSION_DELETE_RESPONSE, Ttl(dayInSeconds)) - try { - if (!sessionStorageRepository.isSessionValid(request.topic)) { - logger.error("Session delete received failure on topic: ${request.topic} - invalid session") - jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) - return@supervisorScope - } - jsonRpcInteractor.unsubscribe(request.topic, - onSuccess = { - logger.log("Session delete received on topic: ${request.topic} - unsubscribe success") - try { - crypto.removeKeys(request.topic.value) - } catch (e: Exception) { - logger.error("Remove keys exception:$e") - } - }, - onFailure = { error -> logger.error("Session delete received on topic: ${request.topic} - unsubscribe error $error") }) - sessionStorageRepository.deleteSession(request.topic) - logger.log("Session delete received on topic: ${request.topic} - emitting") - _events.emit(params.toEngineDO(request.topic)) - } catch (e: Exception) { - logger.error("Session delete received failure on topic: ${request.topic} - $e") - jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot delete a session: ${e.message}, topic: ${request.topic}"), irnParams) - _events.emit(SDKError(e)) - return@supervisorScope - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionEventUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionEventUseCase.kt deleted file mode 100644 index e08efc62f..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionEventUseCase.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.PeerError -import com.walletconnect.sign.common.model.type.Sequences -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDOEvent -import com.walletconnect.sign.engine.model.mapper.toPeerError -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionEventUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: SignParams.EventParams) = supervisorScope { - logger.log("Session event received on topic: ${request.topic}") - val irnParams = IrnParams(Tags.SESSION_EVENT_RESPONSE, Ttl(fiveMinutesInSeconds)) - try { - SignValidator.validateEvent(params.toEngineDOEvent()) { error -> - logger.error("Session event received failure on topic: ${request.topic} - $error") - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - if (!sessionStorageRepository.isSessionValid(request.topic)) { - logger.error("Session event received failure on topic: ${request.topic} - invalid session") - jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) - return@supervisorScope - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) - if (!session.isPeerController) { - logger.error("Session event received failure on topic: ${request.topic} - unauthorized peer") - jsonRpcInteractor.respondWithError(request, PeerError.Unauthorized.Event(Sequences.SESSION.name), irnParams) - return@supervisorScope - } - if (!session.isAcknowledged) { - logger.error("Session event received failure on topic: ${request.topic} - no matching topic") - jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) - return@supervisorScope - } - - val event = params.event - SignValidator.validateChainIdWithEventAuthorisation(params.chainId, event.name, session.sessionNamespaces) { error -> - logger.error("Session event received failure on topic: ${request.topic} - $error") - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - jsonRpcInteractor.respondWithSuccess(request, irnParams) - logger.log("Session event received on topic: ${request.topic} - emitting") - _events.emit(params.toEngineDO(request.topic)) - } catch (e: Exception) { - logger.error("Session event received failure on topic: ${request.topic} - $e") - jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot emit an event: ${e.message}, topic: ${request.topic}"), irnParams) - _events.emit(SDKError(e)) - return@supervisorScope - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionExtendUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionExtendUseCase.kt deleted file mode 100644 index 3780fa067..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionExtendUseCase.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.type.Sequences -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.mapper.toEngineDOSessionExtend -import com.walletconnect.sign.engine.model.mapper.toPeerError -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionExtendUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, requestParams: SignParams.ExtendParams) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_EXTEND_RESPONSE, Ttl(dayInSeconds)) - logger.log("Session extend received on topic: ${request.topic}") - try { - if (!sessionStorageRepository.isSessionValid(request.topic)) { - logger.error("Session extend received failure on topic: ${request.topic}") - jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) - return@supervisorScope - } - - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) - val newExpiry = requestParams.expiry - SignValidator.validateSessionExtend(newExpiry, session.expiry.seconds) { error -> - logger.error("Session extend received failure on topic: ${request.topic} - invalid request: $error") - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - sessionStorageRepository.extendSession(request.topic, newExpiry) - jsonRpcInteractor.respondWithSuccess(request, irnParams) - logger.log("Session extend received on topic: ${request.topic} - emitting") - _events.emit(session.toEngineDOSessionExtend(Expiry(newExpiry))) - } catch (e: Exception) { - logger.error("Session extend received failure on topic: ${request.topic}: $e") - jsonRpcInteractor.respondWithError(request, Uncategorized.GenericError("Cannot update a session: ${e.message}, topic: ${request.topic}"), irnParams) - _events.emit(SDKError(e)) - return@supervisorScope - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionProposalUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionProposalUseCase.kt deleted file mode 100644 index d5a28e19f..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionProposalUseCase.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.engine.model.mapper.toPeerError -import com.walletconnect.sign.engine.model.mapper.toVO -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope -import org.koin.core.qualifier.named - -internal class OnSessionProposalUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val proposalStorageRepository: ProposalStorageRepository, - private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, - private val pairingController: PairingControllerInterface, - private val insertEventUseCase: InsertTelemetryEventUseCase, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - private val isAuthenticateEnabled: Boolean by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.ENABLE_AUTHENTICATE)) } - - suspend operator fun invoke(request: WCRequest, payloadParams: SignParams.SessionProposeParams) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_PROPOSE_RESPONSE_AUTO_REJECT, Ttl(fiveMinutesInSeconds)) - try { - if (isSessionAuthenticateImplemented(request)) { - logger.error("Session proposal received error: pairing supports authenticated sessions") - return@supervisorScope - } - logger.log("Session proposal received: ${request.topic}") - SignValidator.validateProposalNamespaces(payloadParams.requiredNamespaces) { error -> - logger.error("Session proposal received error: required namespace validation: ${error.message}") - insertEventUseCase(Props(type = EventType.Error.REQUIRED_NAMESPACE_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - SignValidator.validateProposalNamespaces(payloadParams.optionalNamespaces ?: emptyMap()) { error -> - logger.error("Session proposal received error: optional namespace validation: ${error.message}") - insertEventUseCase(Props(type = EventType.Error.OPTIONAL_NAMESPACE_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - payloadParams.properties?.let { - SignValidator.validateProperties(payloadParams.properties) { error -> - logger.error("Session proposal received error: session properties validation: ${error.message}") - insertEventUseCase(Props(type = EventType.Error.SESSION_PROPERTIES_VALIDATION_FAILURE, properties = Properties(topic = request.topic.value))) - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - } - proposalStorageRepository.insertProposal(payloadParams.toVO(request.topic, request.id)) - pairingController.setRequestReceived(Core.Params.RequestReceived(request.topic.value)) - val url = payloadParams.proposer.metadata.url - - logger.log("Resolving session proposal attestation: ${System.currentTimeMillis()}") - resolveAttestationIdUseCase(request, url, linkMode = request.transportType == TransportType.LINK_MODE, appLink = payloadParams.proposer.metadata.redirect?.universal) { verifyContext -> - logger.log("Session proposal attestation resolved: ${System.currentTimeMillis()}") - val sessionProposalEvent = EngineDO.SessionProposalEvent(proposal = payloadParams.toEngineDO(request.topic), context = verifyContext.toEngineDO()) - logger.log("Session proposal received on topic: ${request.topic} - emitting") - scope.launch { _events.emit(sessionProposalEvent) } - } - } catch (e: Exception) { - logger.error("Session proposal received error: $e") - jsonRpcInteractor.respondWithError( - request, - Uncategorized.GenericError("Cannot handle a session proposal: ${e.message}, topic: ${request.topic}"), - irnParams - ) - _events.emit(SDKError(e)) - } - } - - private fun isSessionAuthenticateImplemented(request: WCRequest): Boolean = - pairingController.getPairingByTopic(request.topic)?.methods?.contains(JsonRpcMethod.WC_SESSION_AUTHENTICATE) == true && isAuthenticateEnabled -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionRequestUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionRequestUseCase.kt deleted file mode 100644 index 75f15d746..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionRequestUseCase.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.exception.Invalid -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.CoreValidator.isExpired -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.android.verify.domain.ResolveAttestationIdUseCase -import com.walletconnect.android.verify.model.VerifyContext -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.type.Sequences -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.engine.model.mapper.toPeerError -import com.walletconnect.sign.engine.sessionRequestEventsQueue -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.Empty -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class OnSessionRequestUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val resolveAttestationIdUseCase: ResolveAttestationIdUseCase, - private val insertEventUseCase: InsertEventUseCase, - private val clientId: String, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: SignParams.SessionRequestParams) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_REQUEST_RESPONSE, Ttl(fiveMinutesInSeconds)) - logger.log("Session request received on topic: ${request.topic}") - - try { - params.request.expiryTimestamp?.let { - if (Expiry(it).isExpired()) { - logger.error("Session request received failure on topic: ${request.topic} - request expired") - jsonRpcInteractor.respondWithError(request, Invalid.RequestExpired, irnParams) - return@supervisorScope - } - } - - SignValidator.validateSessionRequest(params.toEngineDO(request.topic)) { error -> - logger.error("Session request received failure on topic: ${request.topic} - invalid request") - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - if (!sessionStorageRepository.isSessionValid(request.topic)) { - logger.error("Session request received failure on topic: ${request.topic} - invalid session") - jsonRpcInteractor.respondWithError( - request, - Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), - irnParams - ) - return@supervisorScope - } - val (sessionNamespaces: Map, sessionPeerAppMetaData: AppMetaData?) = - sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) - .run { - val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) - this.sessionNamespaces to peerAppMetaData - } - - val method = params.request.method - SignValidator.validateChainIdWithMethodAuthorisation(params.chainId, method, sessionNamespaces) { error -> - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - if (request.transportType == TransportType.LINK_MODE) { - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_REQUEST_LINK_MODE.id.toString(), - Properties(correlationId = request.id, clientId = clientId, direction = Direction.RECEIVED.state) - ) - ) - } - - val url = sessionPeerAppMetaData?.url ?: String.Empty - logger.log("Resolving session request attestation: ${System.currentTimeMillis()}") - resolveAttestationIdUseCase(request, url, linkMode = request.transportType == TransportType.LINK_MODE, appLink = sessionPeerAppMetaData?.redirect?.universal) { verifyContext -> - logger.log("Session request attestation resolved: ${System.currentTimeMillis()}") - emitSessionRequest(params, request, sessionPeerAppMetaData, verifyContext) - } - } catch (e: Exception) { - logger.error("Session request received failure on topic: ${request.topic} - ${e.message}") - jsonRpcInteractor.respondWithError( - request, - Uncategorized.GenericError("Cannot handle a session request: ${e.message}, topic: ${request.topic}"), - irnParams - ) - _events.emit(SDKError(e)) - return@supervisorScope - } - } - - private fun emitSessionRequest( - params: SignParams.SessionRequestParams, - request: WCRequest, - sessionPeerAppMetaData: AppMetaData?, - verifyContext: VerifyContext - ) { - val sessionRequestEvent = EngineDO.SessionRequestEvent(params.toEngineDO(request, sessionPeerAppMetaData), verifyContext.toEngineDO()) - val event = if (sessionRequestEventsQueue.isEmpty()) { - sessionRequestEvent - } else { - sessionRequestEventsQueue.find { event -> if (event.request.expiry != null) !event.request.expiry.isExpired() else true } ?: sessionRequestEvent - } - - sessionRequestEventsQueue.add(sessionRequestEvent) - logger.log("Session request received on topic: ${request.topic} - emitting") - scope.launch { _events.emit(event) } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionSettleUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionSettleUseCase.kt deleted file mode 100644 index 689a1e3f1..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionSettleUseCase.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.android.utils.toClient -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.PeerError -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.mapper.toPeerError -import com.walletconnect.sign.engine.model.mapper.toSessionApproved -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.Empty -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionSettleUseCase( - private val crypto: KeyManagementRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val proposalStorageRepository: ProposalStorageRepository, - private val sessionStorageRepository: SessionStorageRepository, - private val pairingController: PairingControllerInterface, - private val selfAppMetaData: AppMetaData, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, settleParams: SignParams.SessionSettleParams) = supervisorScope { - logger.log("Session settle received on topic: ${request.topic}") - val sessionTopic = request.topic - val irnParams = IrnParams(Tags.SESSION_SETTLE_RESPONSE, Ttl(fiveMinutesInSeconds)) - val selfPublicKey: PublicKey = try { - crypto.getSelfPublicFromKeyAgreement(sessionTopic) - } catch (e: Exception) { - logger.error("Session settle received failure: ${request.topic}, error: $e") - jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) - return@supervisorScope - } - - val peerMetadata = settleParams.controller.metadata - val proposal = try { - proposalStorageRepository.getProposalByKey(selfPublicKey.keyAsHex).also { proposalStorageRepository.deleteProposal(selfPublicKey.keyAsHex) } - } catch (e: Exception) { - logger.error("Session settle received failure: ${request.topic}, error: $e") - jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) - return@supervisorScope - } - - val (requiredNamespaces, optionalNamespaces, properties) = proposal.run { Triple(requiredNamespaces, optionalNamespaces, properties) } - SignValidator.validateSessionNamespace(settleParams.namespaces, requiredNamespaces) { error -> - logger.error("Session settle received failure - namespace validation: ${request.topic}, error: $error") - jsonRpcInteractor.respondWithError(request, error.toPeerError(), irnParams) - return@supervisorScope - } - - try { - val session = SessionVO.createAcknowledgedSession( - sessionTopic, - settleParams, - selfPublicKey, - selfAppMetaData, - requiredNamespaces, - optionalNamespaces, - properties, - proposal.pairingTopic.value - ) - - sessionStorageRepository.insertSession(session, request.id) - pairingController.updateMetadata(Core.Params.UpdateMetadata(proposal.pairingTopic.value, peerMetadata.toClient(), AppMetaDataType.PEER)) - metadataStorageRepository.insertOrAbortMetadata(sessionTopic, peerMetadata, AppMetaDataType.PEER) - jsonRpcInteractor.respondWithSuccess(request, irnParams) - logger.log("Session settle received on topic: ${request.topic} - emitting") - _events.emit(session.toSessionApproved()) - } catch (e: Exception) { - logger.error("Session settle received failure: ${request.topic}, error: $e") - proposalStorageRepository.insertProposal(proposal) - sessionStorageRepository.deleteSession(sessionTopic) - jsonRpcInteractor.respondWithError(request, PeerError.Failure.SessionSettlementFailed(e.message ?: String.Empty), irnParams) - _events.emit(SDKError(e)) - return@supervisorScope - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt deleted file mode 100644 index ef8b5e2a9..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/requests/OnSessionUpdateUseCase.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.walletconnect.sign.engine.use_case.requests - -import com.walletconnect.android.internal.common.exception.Uncategorized -import com.walletconnect.android.internal.common.model.IrnParams -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.WCRequest -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.foundation.common.model.Ttl -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.PeerError -import com.walletconnect.sign.common.model.type.Sequences -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toMapOfEngineNamespacesSession -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.utils.extractTimestamp -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionUpdateUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val sessionStorageRepository: SessionStorageRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(request: WCRequest, params: SignParams.UpdateNamespacesParams) = supervisorScope { - val irnParams = IrnParams(Tags.SESSION_UPDATE_RESPONSE, Ttl(dayInSeconds)) - logger.log("Session update received on topic: ${request.topic}") - try { - if (!sessionStorageRepository.isSessionValid(request.topic)) { - logger.error("Session update received failure on topic: ${request.topic} - invalid session") - jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic(Sequences.SESSION.name, request.topic.value), irnParams) - return@supervisorScope - } - - val session: SessionVO = sessionStorageRepository.getSessionWithoutMetadataByTopic(request.topic) - if (!session.isPeerController) { - logger.error("Session update received failure on topic: ${request.topic} - unauthorized peer") - jsonRpcInteractor.respondWithError(request, PeerError.Unauthorized.UpdateRequest(Sequences.SESSION.name), irnParams) - return@supervisorScope - } - - SignValidator.validateSessionNamespace(params.namespaces, session.requiredNamespaces) { error -> - logger.error("Session update received failure on topic: ${request.topic} - namespaces validation: $error") - jsonRpcInteractor.respondWithError(request, PeerError.Invalid.UpdateRequest(error.message), irnParams) - return@supervisorScope - } - - if (!sessionStorageRepository.isUpdatedNamespaceValid(session.topic.value, request.id.extractTimestamp())) { - logger.error("Session update received failure on topic: ${request.topic} - Update Namespace Request ID too old") - jsonRpcInteractor.respondWithError(request, PeerError.Invalid.UpdateRequest("Update Namespace Request ID too old"), irnParams) - return@supervisorScope - } - - sessionStorageRepository.deleteNamespaceAndInsertNewNamespace(session.topic.value, params.namespaces, request.id) - jsonRpcInteractor.respondWithSuccess(request, irnParams) - logger.log("Session update received on topic: ${request.topic} - emitting") - _events.emit(EngineDO.SessionUpdateNamespaces(request.topic, params.namespaces.toMapOfEngineNamespacesSession())) - } catch (e: Exception) { - logger.error("Session update received failure on topic: ${request.topic} - $e") - jsonRpcInteractor.respondWithError( - request, - PeerError.Invalid.UpdateRequest("Updating Namespace Failed. Review Namespace structure. Error: ${e.message}, topic: ${request.topic}"), - irnParams - ) - _events.emit(SDKError(e)) - return@supervisorScope - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt deleted file mode 100644 index 4499df4fd..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionProposalResponseUseCase.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.walletconnect.sign.engine.use_case.responses - -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.params.CoreSignParams -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.scope -import com.walletconnect.android.pairing.handler.PairingControllerInterface -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope - -internal class OnSessionProposalResponseUseCase( - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val pairingController: PairingControllerInterface, - private val crypto: KeyManagementRepository, - private val proposalStorageRepository: ProposalStorageRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, params: SignParams.SessionProposeParams) = supervisorScope { - try { - logger.log("Session proposal response received on topic: ${wcResponse.topic}") - val pairingTopic = wcResponse.topic - pairingController.deleteAndUnsubscribePairing(Core.Params.Delete(pairingTopic.value)) - when (val response = wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - logger.log("Session proposal approval received on topic: ${wcResponse.topic}") - val selfPublicKey = PublicKey(params.proposer.publicKey) - val approveParams = response.result as CoreSignParams.ApprovalParams - val responderPublicKey = PublicKey(approveParams.responderPublicKey) - val sessionTopic = crypto.generateTopicFromKeyAgreement(selfPublicKey, responderPublicKey) - - jsonRpcInteractor.subscribe(sessionTopic, - onSuccess = { logger.log("Session proposal approval subscribed on session topic: $sessionTopic") }, - onFailure = { error -> - logger.error("Session proposal approval subscribe error on session topic: $sessionTopic - $error") - scope.launch { _events.emit(SDKError(error)) } - } - ) - } - - is JsonRpcResponse.JsonRpcError -> { - proposalStorageRepository.deleteProposal(params.proposer.publicKey) - logger.log("Session proposal rejection received on topic: ${wcResponse.topic}") - _events.emit(EngineDO.SessionRejected(pairingTopic.value, response.errorMessage)) - } - } - } catch (e: Exception) { - logger.error("Session proposal response received failure on topic: ${wcResponse.topic}: $e") - _events.emit(SDKError(e)) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt deleted file mode 100644 index 959caf800..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionRequestResponseUseCase.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.walletconnect.sign.engine.use_case.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.Tags -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.model.Direction -import com.walletconnect.android.pulse.model.EventType -import com.walletconnect.android.pulse.model.properties.Properties -import com.walletconnect.android.pulse.model.properties.Props -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.json_rpc.domain.GetSessionRequestByIdUseCase -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionRequestResponseUseCase( - private val logger: Logger, - private val insertEventUseCase: InsertEventUseCase, - private val getSessionRequestByIdUseCase: GetSessionRequestByIdUseCase, - private val clientId: String -) { - - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse, params: SignParams.SessionRequestParams) = supervisorScope { - try { - val jsonRpcHistoryEntry = getSessionRequestByIdUseCase(wcResponse.response.id) - logger.log("Session request response received on topic: ${wcResponse.topic}") - val result = when (wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> (wcResponse.response as JsonRpcResponse.JsonRpcResult).toEngineDO() - is JsonRpcResponse.JsonRpcError -> (wcResponse.response as JsonRpcResponse.JsonRpcError).toEngineDO() - } - - if (jsonRpcHistoryEntry?.transportType == TransportType.LINK_MODE) { - insertEventUseCase( - Props( - EventType.SUCCESS, - Tags.SESSION_REQUEST_LINK_MODE_RESPONSE.id.toString(), - Properties(correlationId = wcResponse.response.id, clientId = clientId, direction = Direction.RECEIVED.state) - ) - ) - } - val method = params.request.method - logger.log("Session request response received on topic: ${wcResponse.topic} - emitting: $result") - _events.emit(EngineDO.SessionPayloadResponse(wcResponse.topic.value, params.chainId, method, result)) - } catch (e: Exception) { - logger.error("Session request response received failure: $e") - _events.emit(SDKError(e)) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt deleted file mode 100644 index b0465b415..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/engine/use_case/responses/OnSessionSettleResponseUseCase.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.walletconnect.sign.engine.use_case.responses - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.model.SDKError -import com.walletconnect.android.internal.common.model.WCResponse -import com.walletconnect.android.internal.common.model.type.EngineEvent -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toEngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.supervisorScope - -internal class OnSessionSettleResponseUseCase( - private val sessionStorageRepository: SessionStorageRepository, - private val jsonRpcInteractor: RelayJsonRpcInteractorInterface, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, - private val crypto: KeyManagementRepository, - private val logger: Logger -) { - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow = _events.asSharedFlow() - - suspend operator fun invoke(wcResponse: WCResponse) = supervisorScope { - try { - logger.log("Session settle response received on topic: ${wcResponse.topic}") - val sessionTopic = wcResponse.topic - if (!sessionStorageRepository.isSessionValid(sessionTopic)) { - logger.error("Peer failed to settle session: invalid session") - return@supervisorScope - } - val session = sessionStorageRepository.getSessionWithoutMetadataByTopic(sessionTopic).run { - val peerAppMetaData = metadataStorageRepository.getByTopicAndType(this.topic, AppMetaDataType.PEER) - this.copy(selfAppMetaData = selfAppMetaData, peerAppMetaData = peerAppMetaData) - } - - when (wcResponse.response) { - is JsonRpcResponse.JsonRpcResult -> { - logger.log("Session settle success received") - sessionStorageRepository.acknowledgeSession(sessionTopic) - _events.emit(EngineDO.SettledSessionResponse.Result(session.toEngineDO())) - } - - is JsonRpcResponse.JsonRpcError -> { - logger.error("Peer failed to settle session: ${(wcResponse.response as JsonRpcResponse.JsonRpcError).errorMessage}") - jsonRpcInteractor.unsubscribe(sessionTopic, onSuccess = { - runCatching { - sessionStorageRepository.deleteSession(sessionTopic) - crypto.removeKeys(sessionTopic.value) - }.onFailure { logger.error(it) } - }) - } - } - } catch (e: Exception) { - logger.error("Peer failed to settle session: $e") - _events.emit(SDKError(e)) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt deleted file mode 100644 index 8fef1eb48..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/DeleteRequestByIdUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import kotlinx.coroutines.supervisorScope - -internal class DeleteRequestByIdUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val verifyContextStorageRepository: VerifyContextStorageRepository -) { - - suspend operator fun invoke(id: Long) { - supervisorScope { - jsonRpcHistory.deleteRecordById(id) - verifyContextStorageRepository.delete(id) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt deleted file mode 100644 index 9b1dc3c14..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingJsonRpcHistoryEntryByIdUseCase.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.toRequest - -internal class GetPendingJsonRpcHistoryEntryByIdUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - - operator fun invoke(id: Long): Request? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) - var entry: Request? = null - - if (record != null) { - val sessionRequest: SignRpc.SessionRequest? = serializer.tryDeserialize(record.body) - if (sessionRequest != null) { - entry = record.toRequest(sessionRequest.params) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt deleted file mode 100644 index cf489b8d1..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingRequestsByTopicUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.json_rpc.model.toRequest -import kotlinx.coroutines.supervisorScope - -internal class GetPendingRequestsUseCaseByTopic( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) : GetPendingRequestsUseCaseByTopicInterface { - - override suspend fun getPendingRequests(topic: Topic): List> = supervisorScope { - jsonRpcHistory.getListOfPendingRecordsByTopic(topic) - .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } - .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } - } -} - -internal interface GetPendingRequestsUseCaseByTopicInterface { - suspend fun getPendingRequests(topic: Topic): List> -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt deleted file mode 100644 index 557ffceb8..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionAuthenticateRequest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.toRequest - -class GetPendingSessionAuthenticateRequest( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - - internal operator fun invoke(id: Long): Request? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getPendingRecordById(id) - var entry: Request? = null - - if (record != null) { - val authRequest: SignRpc.SessionAuthenticate? = serializer.tryDeserialize(record.body) - if (authRequest != null) { - entry = record.toRequest(authRequest.params) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt deleted file mode 100644 index f605c989b..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequestByTopicUseCase.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.model.AppMetaDataType -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toSessionRequest -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.json_rpc.model.toRequest -import kotlinx.coroutines.supervisorScope - -internal class GetPendingSessionRequestByTopicUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer, - private val metadataStorageRepository: MetadataStorageRepositoryInterface, -) : GetPendingSessionRequestByTopicUseCaseInterface { - - override suspend fun getPendingSessionRequests(topic: Topic): List = supervisorScope { - jsonRpcHistory.getListOfPendingRecordsByTopic(topic) - .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } - .mapNotNull { record -> - serializer.tryDeserialize(record.body)?.toRequest(record) - ?.toSessionRequest(metadataStorageRepository.getByTopicAndType(Topic(record.topic), AppMetaDataType.PEER)) - } - } -} - -internal interface GetPendingSessionRequestByTopicUseCaseInterface { - suspend fun getPendingSessionRequests(topic: Topic): List -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequests.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequests.kt deleted file mode 100644 index b9e888edd..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetPendingSessionRequests.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.json_rpc.model.JsonRpcMethod -import com.walletconnect.sign.json_rpc.model.toRequest -import kotlinx.coroutines.supervisorScope - -internal class GetPendingSessionRequests( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - - suspend operator fun invoke(): List> = supervisorScope { - jsonRpcHistory.getListOfPendingRecords() - .filter { record -> record.method == JsonRpcMethod.WC_SESSION_REQUEST } - .mapNotNull { record -> serializer.tryDeserialize(record.body)?.toRequest(record) } - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt deleted file mode 100644 index 169f35281..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionAuthenticateRequest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.toRequest - -internal class GetSessionAuthenticateRequest( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - internal operator fun invoke(id: Long): Request? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getRecordById(id) - var entry: Request? = null - - if (record != null) { - val authRequest: SignRpc.SessionAuthenticate? = serializer.tryDeserialize(record.body) - if (authRequest != null) { - entry = record.toRequest(authRequest.params) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt deleted file mode 100644 index 045ee6770..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/domain/GetSessionRequestByIdUseCase.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.walletconnect.sign.json_rpc.domain - -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.model.toRequest - -internal class GetSessionRequestByIdUseCase( - private val jsonRpcHistory: JsonRpcHistory, - private val serializer: JsonRpcSerializer -) { - operator fun invoke(id: Long): Request? { - val record: JsonRpcHistoryRecord? = jsonRpcHistory.getRecordById(id) - var entry: Request? = null - - if (record != null) { - val sessionRequest: SignRpc.SessionRequest? = serializer.tryDeserialize(record.body) - if (sessionRequest != null) { - entry = record.toRequest(sessionRequest.params) - } - } - - return entry - } -} \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMapper.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMapper.kt deleted file mode 100644 index 3d0ab67c7..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMapper.kt +++ /dev/null @@ -1,57 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.json_rpc.model - -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams - -@JvmSynthetic -internal fun SignRpc.SessionRequest.toRequest(entry: JsonRpcHistoryRecord): Request = - Request( - entry.id, - Topic(entry.topic), - params.request.method, - params.chainId, - params.request.params, - if (params.request.expiryTimestamp != null) Expiry(params.request.expiryTimestamp) else null, - transportType = entry.transportType - ) - -@JvmSynthetic -internal fun JsonRpcHistoryRecord.toRequest(params: SignParams.SessionRequestParams): Request = - Request( - id, - Topic(topic), - method, - params.chainId, - params, - transportType = transportType - ) - -@JvmSynthetic -internal fun JsonRpcHistoryRecord.toRequest(params: SignParams.SessionAuthenticateParams): Request = - Request( - id, - Topic(topic), - method, - null, - params, - Expiry(params.expiryTimestamp), - transportType = transportType - ) - -@JvmSynthetic -internal fun SignRpc.SessionAuthenticate.toRequest(entry: JsonRpcHistoryRecord): Request = - Request( - entry.id, - Topic(entry.topic), - entry.method, - null, - params, - Expiry(params.expiryTimestamp), - transportType = entry.transportType - ) \ No newline at end of file diff --git a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMethod.kt b/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMethod.kt deleted file mode 100644 index 0f717e04a..000000000 --- a/protocol/sign/src/main/kotlin/com/walletconnect/sign/json_rpc/model/JsonRpcMethod.kt +++ /dev/null @@ -1,24 +0,0 @@ -@file:JvmSynthetic - -package com.walletconnect.sign.json_rpc.model - -internal object JsonRpcMethod { - @get:JvmSynthetic - const val WC_SESSION_PROPOSE: String = "wc_sessionPropose" - @get:JvmSynthetic - const val WC_SESSION_AUTHENTICATE: String = "wc_sessionAuthenticate" - @get:JvmSynthetic - const val WC_SESSION_SETTLE: String = "wc_sessionSettle" - @get:JvmSynthetic - const val WC_SESSION_REQUEST: String = "wc_sessionRequest" - @get:JvmSynthetic - const val WC_SESSION_DELETE: String = "wc_sessionDelete" - @get:JvmSynthetic - const val WC_SESSION_PING: String = "wc_sessionPing" - @get:JvmSynthetic - const val WC_SESSION_EVENT: String = "wc_sessionEvent" - @get:JvmSynthetic - const val WC_SESSION_UPDATE: String = "wc_sessionUpdate" - @get:JvmSynthetic - const val WC_SESSION_EXTEND: String = "wc_sessionExtend" -} \ No newline at end of file diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/authenticate_reponse/AuthenticateResponseTopicDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/authenticate_reponse/AuthenticateResponseTopicDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/authenticate_reponse/AuthenticateResponseTopicDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/authenticate_reponse/AuthenticateResponseTopicDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/link_mode/LinkModeDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/link_mode/LinkModeDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/link_mode/LinkModeDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/link_mode/LinkModeDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/namespace/NamespaceDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/namespace/NamespaceDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/namespace/NamespaceDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/namespace/NamespaceDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/optional_namespaces/OptionalNamespaceDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/optional_namespaces/OptionalNamespaceDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/optional_namespaces/OptionalNamespaceDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/optional_namespaces/OptionalNamespaceDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/proposal/ProposalDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/proposal/ProposalDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/proposal/ProposalDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/proposal/ProposalDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/proposal_namespace/ProposalNamespaceDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/proposal_namespace/ProposalNamespaceDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/proposal_namespace/ProposalNamespaceDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/proposal_namespace/ProposalNamespaceDao.sq diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/session/SessionDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/session/SessionDao.sq similarity index 96% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/session/SessionDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/session/SessionDao.sq index b81964966..47383e2af 100644 --- a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/session/SessionDao.sq +++ b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/session/SessionDao.sq @@ -1,7 +1,7 @@ import kotlin.Boolean; import kotlin.String; import kotlin.collections.Map; -import com.walletconnect.android.internal.common.model.TransportType; +import com.reown.android.internal.common.model.TransportType; CREATE TABLE SessionDao( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, diff --git a/protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/temp/TempNamespaceDao.sq b/protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/temp/TempNamespaceDao.sq similarity index 100% rename from protocol/sign/src/main/sqldelight/com/walletconnect/sign/storage/data/dao/temp/TempNamespaceDao.sq rename to protocol/sign/src/main/sqldelight/com/reown/sign/storage/data/dao/temp/TempNamespaceDao.sq diff --git a/protocol/sign/src/main/sqldelight/migrations/1.sqm b/protocol/sign/src/main/sqldelight/migrations/1.sqm index 23981e44b..fce322b12 100644 --- a/protocol/sign/src/main/sqldelight/migrations/1.sqm +++ b/protocol/sign/src/main/sqldelight/migrations/1.sqm @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.AppMetaDataType; +import com.reown.android.internal.common.model.AppMetaDataType; import kotlin.Boolean; import kotlin.String; import kotlin.collections.List; diff --git a/protocol/sign/src/main/sqldelight/migrations/2.sqm b/protocol/sign/src/main/sqldelight/migrations/2.sqm index ccf652f61..c99bbe7f7 100644 --- a/protocol/sign/src/main/sqldelight/migrations/2.sqm +++ b/protocol/sign/src/main/sqldelight/migrations/2.sqm @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.AppMetaDataType; +import com.reown.android.internal.common.model.AppMetaDataType; import kotlin.Boolean; import kotlin.String; import kotlin.collections.List; diff --git a/protocol/sign/src/main/sqldelight/migrations/3.sqm b/protocol/sign/src/main/sqldelight/migrations/3.sqm index 436ed436c..8e910664e 100644 --- a/protocol/sign/src/main/sqldelight/migrations/3.sqm +++ b/protocol/sign/src/main/sqldelight/migrations/3.sqm @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.AppMetaDataType; +import com.reown.android.internal.common.model.AppMetaDataType; import kotlin.Boolean; import kotlin.String; import kotlin.collections.List; diff --git a/protocol/sign/src/main/sqldelight/migrations/4.sqm b/protocol/sign/src/main/sqldelight/migrations/4.sqm index 949586495..bc14f8ca0 100644 --- a/protocol/sign/src/main/sqldelight/migrations/4.sqm +++ b/protocol/sign/src/main/sqldelight/migrations/4.sqm @@ -1,4 +1,4 @@ -import com.walletconnect.android.internal.common.model.AppMetaDataType; +import com.reown.android.internal.common.model.AppMetaDataType; import kotlin.Boolean; import kotlin.String; import kotlin.collections.List; diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/AttestationIdGenerationTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/AttestationIdGenerationTest.kt similarity index 90% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/AttestationIdGenerationTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/AttestationIdGenerationTest.kt index ecbeb1cbc..7589677ba 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/AttestationIdGenerationTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/AttestationIdGenerationTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sign +package com.reown.sign import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.walletconnect.android.internal.common.crypto.sha256 -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SessionProposer -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.android.internal.common.crypto.sha256 +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SessionProposer +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams import junit.framework.TestCase.assertEquals import org.junit.Test diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/DeleteRequestByIdUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/DeleteRequestByIdUseCaseTest.kt similarity index 79% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/DeleteRequestByIdUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/DeleteRequestByIdUseCaseTest.kt index 68b5f22fa..bbb0fb584 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/DeleteRequestByIdUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/DeleteRequestByIdUseCaseTest.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sign +package com.reown.sign -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.sign.json_rpc.domain.DeleteRequestByIdUseCase +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.sign.json_rpc.domain.DeleteRequestByIdUseCase import io.mockk.coVerify import io.mockk.mockk import io.mockk.slot diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/GetSessionRequestByIdUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/GetSessionRequestByIdUseCaseTest.kt similarity index 80% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/GetSessionRequestByIdUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/GetSessionRequestByIdUseCaseTest.kt index d77bb7f56..aabb4f5b8 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/GetSessionRequestByIdUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/GetSessionRequestByIdUseCaseTest.kt @@ -1,12 +1,12 @@ -package com.walletconnect.sign +package com.reown.sign -import com.walletconnect.android.internal.common.json_rpc.data.JsonRpcSerializer -import com.walletconnect.android.internal.common.json_rpc.model.JsonRpcHistoryRecord -import com.walletconnect.android.internal.common.storage.rpc.JsonRpcHistory -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO -import com.walletconnect.sign.json_rpc.domain.GetSessionRequestByIdUseCase +import com.reown.android.internal.common.json_rpc.data.JsonRpcSerializer +import com.reown.android.internal.common.json_rpc.model.JsonRpcHistoryRecord +import com.reown.android.internal.common.storage.rpc.JsonRpcHistory +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.sign.json_rpc.domain.GetSessionRequestByIdUseCase import io.mockk.every import io.mockk.mockk import io.mockk.verify diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/ReCapsTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/ReCapsTest.kt similarity index 99% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/ReCapsTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/ReCapsTest.kt index 7d74159f8..9fd8eeadb 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/ReCapsTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/ReCapsTest.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign +package com.reown.sign import org.bouncycastle.util.encoders.Base64 import org.json.JSONArray diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/GenericPayloadsTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/GenericPayloadsTest.kt similarity index 98% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/GenericPayloadsTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/adapters/GenericPayloadsTest.kt index de681be98..c7ac3e0c0 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/GenericPayloadsTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/GenericPayloadsTest.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.adapters +package com.reown.sign.adapters import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionEventVOJsonAdapterTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionEventVOJsonAdapterTest.kt similarity index 86% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionEventVOJsonAdapterTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionEventVOJsonAdapterTest.kt index 583b6dd94..b8504e9ac 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionEventVOJsonAdapterTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionEventVOJsonAdapterTest.kt @@ -1,12 +1,12 @@ -package com.walletconnect.sign.adapters +package com.reown.sign.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.tinder.scarlet.utils.getRawType -import com.walletconnect.sign.common.adapters.SessionEventVOJsonAdapter -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionEventVO -import com.walletconnect.sign.util.iterateJsonArrays +import com.reown.sign.common.adapters.SessionEventVOJsonAdapter +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionEventVO +import com.reown.sign.util.iterateJsonArrays import junit.framework.TestCase import org.json.JSONArray import org.junit.Test diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestParamsParsingTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestParamsParsingTest.kt similarity index 97% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestParamsParsingTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestParamsParsingTest.kt index 330eaf758..26523de56 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestParamsParsingTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestParamsParsingTest.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.adapters +package com.reown.sign.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestVOJsonAdapterTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestVOJsonAdapterTest.kt similarity index 98% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestVOJsonAdapterTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestVOJsonAdapterTest.kt index 0e5b7c0fa..2409aa1eb 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/adapters/SessionRequestVOJsonAdapterTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/adapters/SessionRequestVOJsonAdapterTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sign.adapters +package com.reown.sign.adapters import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.tinder.scarlet.utils.getRawType -import com.walletconnect.sign.common.adapters.SessionRequestVOJsonAdapter -import com.walletconnect.sign.common.model.vo.clientsync.session.SignRpc -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.common.model.vo.clientsync.session.payload.SessionRequestVO -import com.walletconnect.sign.util.iterateJsonArrays -import com.walletconnect.sign.util.iterateJsonObjects +import com.reown.sign.common.adapters.SessionRequestVOJsonAdapter +import com.reown.sign.common.model.vo.clientsync.session.SignRpc +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.common.model.vo.clientsync.session.payload.SessionRequestVO +import com.reown.sign.util.iterateJsonArrays +import com.reown.sign.util.iterateJsonObjects import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertTrue import org.intellij.lang.annotations.Language diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/SessionRequestQueueTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/SessionRequestQueueTest.kt similarity index 98% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/SessionRequestQueueTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/SessionRequestQueueTest.kt index e2530f4e9..b6ded9281 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/SessionRequestQueueTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/SessionRequestQueueTest.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.engine +package com.reown.sign.engine import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt similarity index 82% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt index 58b18c6da..0b6909b98 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionAuthenticateUseCaseTest.kt @@ -1,34 +1,34 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.RequestExpiredException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.signing.cacao.Cacao -import com.walletconnect.android.internal.common.signing.cacao.CacaoType -import com.walletconnect.android.internal.common.signing.cacao.CacaoVerifier -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.android.pulse.model.Trace -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.MissingSessionAuthenticateRequest -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.common.PayloadParams -import com.walletconnect.sign.common.model.vo.clientsync.common.Requester -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.RequestExpiredException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.signing.cacao.Cacao +import com.reown.android.internal.common.signing.cacao.CacaoType +import com.reown.android.internal.common.signing.cacao.CacaoVerifier +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.android.pulse.model.Trace +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.MissingSessionAuthenticateRequest +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.common.PayloadParams +import com.reown.sign.common.model.vo.clientsync.common.Requester +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest +import com.reown.sign.storage.sequence.SessionStorageRepository +import com.reown.util.bytesToHex +import com.reown.util.randomBytes import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify @@ -168,7 +168,7 @@ class ApproveSessionAuthenticateUseCaseTest { assert(throwable is MissingSessionAuthenticateRequest) }) - coVerify { insertEventUseCase(any()) } + coVerify { insertTelemetryEventUseCase(any()) } } @Test @@ -213,7 +213,7 @@ class ApproveSessionAuthenticateUseCaseTest { assert(throwable is RequestExpiredException) }) - coVerify { insertEventUseCase(any()) } + coVerify { insertTelemetryEventUseCase(any()) } } @Test diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt similarity index 85% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt index 071b3b974..302d79b23 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ApproveSessionUseCaseTest.kt @@ -1,21 +1,21 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.pulse.domain.InsertTelemetryEventUseCase -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.common.exceptions.SessionProposalExpiredException -import com.walletconnect.sign.common.model.vo.proposal.ProposalVO -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.pulse.domain.InsertTelemetryEventUseCase +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.common.exceptions.SessionProposalExpiredException +import com.reown.sign.common.model.vo.proposal.ProposalVO +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.proposal.ProposalStorageRepository +import com.reown.sign.storage.sequence.SessionStorageRepository import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt similarity index 77% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt index ec96a92b6..f6096cb06 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/DisconnectSessionUseCaseTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.util.Logger +import com.reown.sign.storage.sequence.SessionStorageRepository import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCaseTest.kt similarity index 83% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCaseTest.kt index 3dcdf8eec..2670043f0 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/EmitEventUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/EmitEventUseCaseTest.kt @@ -1,13 +1,13 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository import io.mockk.every import io.mockk.mockk import io.mockk.verify diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt similarity index 77% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt index 72de2e774..fdd1ba2d0 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ExtendSessionUseCaseTest.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.NotSettledSessionException -import com.walletconnect.sign.common.model.vo.sequence.SessionVO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.NotSettledSessionException +import com.reown.sign.common.model.vo.sequence.SessionVO +import com.reown.sign.storage.sequence.SessionStorageRepository import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt similarity index 95% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt index 3ff01a26b..51368afa3 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetNamespacesFromReCapsTest.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt similarity index 90% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt index 0d2e26c00..922ded387 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/GetPairingForSessionAuthenticateUseCaseTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.Core -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pairing.client.PairingInterface +import com.reown.android.Core +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pairing.client.PairingInterface import io.mockk.every import io.mockk.mockk import org.junit.Assert.assertThrows diff --git a/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt new file mode 100644 index 000000000..c067c5bae --- /dev/null +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt @@ -0,0 +1,58 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.proposal.ProposalStorageRepository +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertSame +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +class ProposeSessionUseCaseTest { + private val jsonRpcInteractor = mockk() + private val crypto = mockk() + private val proposalStorageRepository = mockk() + private val selfAppMetaData = mockk() + private val logger = mockk() + private val proposeSessionUseCase = ProposeSessionUseCase(jsonRpcInteractor, crypto, proposalStorageRepository, selfAppMetaData, logger) + + @Before + fun setUp() { + every { logger.error(any() as String) } answers { } + every { logger.error(any() as Exception) } answers { } + } + + @Test + fun `onFailure is called when SignValidator validateProposalNamespaces fails`() = runTest { + + proposeSessionUseCase.proposeSession( + requiredNamespaces = mapOf("required" to EngineDO.Namespace.Proposal(listOf("required"), listOf("required"), listOf("required"))), + optionalNamespaces = mapOf("optional" to EngineDO.Namespace.Proposal(listOf("optional"), listOf("optional"), listOf("optional"))), + properties = mapOf("key" to "value"), + pairing = com.reown.android.internal.common.model.Pairing( + topic = Topic("topic"), + relay = RelayProtocolOptions(), + symmetricKey = SymmetricKey("symmetricKey"), + methods = "", + expiry = Expiry(12345678L) + ), + onSuccess = { + fail("onSuccess should not be called since should have validation failed") + }, + onFailure = { error -> + assertSame(InvalidNamespaceException::class, error::class) + } + ) + } +} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeUseCaseTest.kt similarity index 86% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeUseCaseTest.kt index 62fc54232..82681f2b3 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/ProposeUseCaseTest.kt @@ -1,19 +1,19 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Pairing -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Pairing +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.InvalidNamespaceException +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.proposal.ProposalStorageRepository import io.mockk.Called import io.mockk.Runs import io.mockk.coEvery diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt similarity index 78% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt index 68d942136..fa0222570 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RejectSessionAuthenticateUseCaseTest.kt @@ -1,26 +1,26 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.EnvelopeType -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.TransportType -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.MissingSessionAuthenticateRequest -import com.walletconnect.sign.common.model.Request -import com.walletconnect.sign.common.model.vo.clientsync.common.PayloadParams -import com.walletconnect.sign.common.model.vo.clientsync.common.Requester -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.EnvelopeType +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.common.model.TransportType +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.common.exceptions.MissingSessionAuthenticateRequest +import com.reown.sign.common.model.Request +import com.reown.sign.common.model.vo.clientsync.common.PayloadParams +import com.reown.sign.common.model.vo.clientsync.common.Requester +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.json_rpc.domain.GetPendingSessionAuthenticateRequest +import com.reown.util.bytesToHex +import com.reown.util.randomBytes import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify diff --git a/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt new file mode 100644 index 000000000..4410a3afd --- /dev/null +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt @@ -0,0 +1,62 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.JsonRpcResponse +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.internal.common.storage.verify.VerifyContextStorageRepository +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.foundation.util.Logger +import com.reown.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase +import com.reown.sign.storage.sequence.SessionStorageRepository +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Before +import org.junit.Test + +class RespondSessionRequestUseCaseTest { + private val jsonRpcInteractor = mockk() + private val sessionStorageRepository = mockk() + private val getPendingJsonRpcHistoryEntryByIdUseCase = mockk() + private val logger = mockk() + private val verifyContextStorageRepository = mockk() + private val metadataStorageRepository = mockk() + private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface = mockk() + private val insertEventUseCase = mockk() + private val respondSessionRequestUseCase = RespondSessionRequestUseCase( + jsonRpcInteractor, + sessionStorageRepository, + getPendingJsonRpcHistoryEntryByIdUseCase, + linkModeJsonRpcInteractor, + logger, + verifyContextStorageRepository, + metadataStorageRepository, + insertEventUseCase, + "clientId" + ) + + @Before + fun setUp() { + every { logger.error(any() as String) } answers { } + every { logger.error(any() as Exception) } answers { } + } + + @Test + fun `onFailure is called when sessionStorageRepository isSessionValid is false`() = runTest { + every { sessionStorageRepository.isSessionValid(any()) } returns false + + respondSessionRequestUseCase.respondSessionRequest( + topic = "topic", + jsonRpcResponse = JsonRpcResponse.JsonRpcResult(1, "2.0", "result"), + onSuccess = { + Assert.fail("onSuccess should not be called since should have validation failed") + }, + onFailure = { error -> + Assert.assertSame(CannotFindSequenceForTopic::class, error::class) + } + ) + } +} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt similarity index 84% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt index 56a2fbd38..d6e752328 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionAuthenticateUseCaseTest.kt @@ -1,23 +1,23 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.exception.InvalidExpiryException -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.android.internal.common.model.Redirect -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.utils.currentTimeInSeconds -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.foundation.common.model.PublicKey -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.authenticate.AuthenticateResponseTopicRepository -import com.walletconnect.sign.storage.link_mode.LinkModeStorageRepository +import com.reown.android.Core +import com.reown.android.internal.common.crypto.kmr.KeyManagementRepository +import com.reown.android.internal.common.exception.InvalidExpiryException +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.Expiry +import com.reown.android.internal.common.model.Namespace +import com.reown.android.internal.common.model.Redirect +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.utils.currentTimeInSeconds +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.foundation.common.model.PublicKey +import com.reown.foundation.common.model.Topic +import com.reown.foundation.util.Logger +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.authenticate.AuthenticateResponseTopicRepository +import com.reown.sign.storage.link_mode.LinkModeStorageRepository import io.mockk.Runs import io.mockk.clearAllMocks import io.mockk.coEvery diff --git a/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt new file mode 100644 index 000000000..1a0e7981a --- /dev/null +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt @@ -0,0 +1,61 @@ +package com.reown.sign.engine.use_case.calls + +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface +import com.reown.android.pulse.domain.InsertEventUseCase +import com.reown.foundation.util.Logger +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.storage.sequence.SessionStorageRepository +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertSame +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +class SessionRequestUseCaseTest { + private val sessionStorageRepository = mockk() + private val jsonRpcInteractor = mockk() + private val logger = mockk() + private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface = mockk() + private val metadataStorageRepository = mockk() + private val insertEventUseCase = mockk() + private val sessionRequestUseCase = SessionRequestUseCase( + sessionStorageRepository, + jsonRpcInteractor, + linkModeJsonRpcInteractor, + metadataStorageRepository, + insertEventUseCase, + "clientId", + logger + ) + + @Before + fun setUp() { + every { logger.error(any() as String) } answers { } + every { logger.error(any() as Exception) } answers { } + } + + @Test + fun `onFailure is called when sessionStorageRepository isSessionValid is false`() = runTest { + every { sessionStorageRepository.isSessionValid(any()) } returns false + + sessionRequestUseCase.sessionRequest( + request = EngineDO.Request( + topic = "topic", + method = "method", + params = "params", + chainId = "chainId", + ), + onSuccess = { + fail("onSuccess should not be called since should have validation failed") + }, + onFailure = { error -> + assertSame(CannotFindSequenceForTopic::class, error::class) + } + ) + } +} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt similarity index 78% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt index 619c5db3c..b5d729eb9 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/engine/use_case/calls/SessionUpdateUseCaseTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.engine.use_case.calls +package com.reown.sign.engine.use_case.calls -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.storage.sequence.SessionStorageRepository +import com.reown.android.internal.common.exception.CannotFindSequenceForTopic +import com.reown.android.internal.common.model.type.RelayJsonRpcInteractorInterface +import com.reown.foundation.util.Logger +import com.reown.sign.storage.sequence.SessionStorageRepository import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/mappers/EngineMapperTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/mappers/EngineMapperTest.kt similarity index 83% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/mappers/EngineMapperTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/mappers/EngineMapperTest.kt index 2fd4f13da..6d8322312 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/mappers/EngineMapperTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/mappers/EngineMapperTest.kt @@ -1,11 +1,11 @@ -package com.walletconnect.sign.mappers +package com.reown.sign.mappers -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SessionProposer -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.model.vo.clientsync.session.params.SignParams -import com.walletconnect.sign.engine.model.mapper.toEngineDO +import com.reown.android.internal.common.model.AppMetaData +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SessionProposer +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.model.vo.clientsync.session.params.SignParams +import com.reown.sign.engine.model.mapper.toEngineDO import org.junit.Test class EngineMapperTest { diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/CoroutineTestRule.kt b/protocol/sign/src/test/kotlin/com/reown/sign/util/CoroutineTestRule.kt similarity index 96% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/util/CoroutineTestRule.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/util/CoroutineTestRule.kt index 794b06a39..7397dcfe4 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/CoroutineTestRule.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/util/CoroutineTestRule.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sign.util +package com.reown.sign.util import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/GenerateApprovedNamespacesUtilsTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/util/GenerateApprovedNamespacesUtilsTest.kt similarity index 99% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/util/GenerateApprovedNamespacesUtilsTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/util/GenerateApprovedNamespacesUtilsTest.kt index b692c73ab..960809d07 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/GenerateApprovedNamespacesUtilsTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/util/GenerateApprovedNamespacesUtilsTest.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sign.util +package com.reown.sign.util -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.sign.client.Sign -import com.walletconnect.sign.client.utils.generateApprovedNamespaces -import com.walletconnect.sign.client.utils.normalizeNamespaces +import com.reown.android.internal.common.model.Namespace +import com.reown.sign.client.Sign +import com.reown.sign.client.utils.generateApprovedNamespaces +import com.reown.sign.client.utils.normalizeNamespaces import junit.framework.TestCase.assertEquals import org.junit.Assert.assertThrows import org.junit.Test diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/MergeReCapsTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/util/MergeReCapsTest.kt similarity index 94% rename from protocol/sign/src/test/kotlin/com/walletconnect/sign/util/MergeReCapsTest.kt rename to protocol/sign/src/test/kotlin/com/reown/sign/util/MergeReCapsTest.kt index 7c10fa008..9eb0d7ae6 100644 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/MergeReCapsTest.kt +++ b/protocol/sign/src/test/kotlin/com/reown/sign/util/MergeReCapsTest.kt @@ -1,6 +1,6 @@ -package com.walletconnect.sign.util +package com.reown.sign.util -import com.walletconnect.android.internal.common.signing.cacao.mergeReCaps +import com.reown.android.internal.common.signing.cacao.mergeReCaps import org.json.JSONObject import org.junit.Test diff --git a/protocol/sign/src/test/kotlin/com/reown/sign/util/UtilFunctions.kt b/protocol/sign/src/test/kotlin/com/reown/sign/util/UtilFunctions.kt new file mode 100644 index 000000000..5da0fee60 --- /dev/null +++ b/protocol/sign/src/test/kotlin/com/reown/sign/util/UtilFunctions.kt @@ -0,0 +1,39 @@ +package com.reown.sign.util + +import junit.framework.TestCase +import org.json.JSONArray +import org.json.JSONObject +internal fun iterateJsonArrays(expJsonArray: JSONArray, actJsonArray: JSONArray) { + TestCase.assertEquals(expJsonArray.length(), actJsonArray.length()) + + (0 until expJsonArray.length()).forEach { index -> + val expCurrentIndexItem = expJsonArray.get(index) + val actCurrentIndexItem = actJsonArray[index] + + when { + expCurrentIndexItem is JSONObject && actCurrentIndexItem is JSONObject -> iterateJsonObjects(expCurrentIndexItem, actCurrentIndexItem) + expCurrentIndexItem is JSONArray && actCurrentIndexItem is JSONArray -> iterateJsonArrays(expCurrentIndexItem, actCurrentIndexItem) + else -> TestCase.assertEquals(expCurrentIndexItem, actCurrentIndexItem) + } + } +} + +internal fun iterateJsonObjects(expJsonObject: JSONObject, actJsonObject: JSONObject) { + expJsonObject.keys().forEach { key -> + assert(actJsonObject.has(key)) + val expCurrentItem = expJsonObject.get(key) + val actCurrentItem = actJsonObject.get(key) + + when { + expCurrentItem is JSONObject && actCurrentItem is JSONObject -> { + iterateJsonObjects(expCurrentItem, actCurrentItem) + } + + expCurrentItem is JSONArray && actCurrentItem is JSONArray -> { + iterateJsonArrays(expCurrentItem, actCurrentItem) + } + + else -> TestCase.assertEquals(expCurrentItem, actCurrentItem) + } + } +} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/reown/sign/validator/ValidatorTest.kt b/protocol/sign/src/test/kotlin/com/reown/sign/validator/ValidatorTest.kt new file mode 100644 index 000000000..9b69c5bb6 --- /dev/null +++ b/protocol/sign/src/test/kotlin/com/reown/sign/validator/ValidatorTest.kt @@ -0,0 +1,1124 @@ +package com.reown.sign.validator + +import com.reown.android.internal.common.model.RelayProtocolOptions +import com.reown.android.internal.common.model.SymmetricKey +import com.reown.android.internal.utils.dayInSeconds +import com.reown.android.internal.utils.fiveMinutesInSeconds +import com.reown.android.internal.utils.monthInSeconds +import com.reown.android.internal.utils.weekInSeconds +import com.reown.foundation.common.model.Topic +import com.reown.sign.common.exceptions.EMPTY_NAMESPACES_MESSAGE +import com.reown.sign.common.exceptions.INVALID_EVENT_MESSAGE +import com.reown.sign.common.exceptions.INVALID_EXTEND_TIME +import com.reown.sign.common.exceptions.INVALID_REQUEST_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_CAIP_2_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_EVENTS_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_KEYS_INVALID_FORMAT +import com.reown.sign.common.exceptions.NAMESPACE_KEYS_MISSING_MESSAGE +import com.reown.sign.common.exceptions.NAMESPACE_METHODS_MISSING_MESSAGE +import com.reown.sign.common.exceptions.UNAUTHORIZED_EVENT_MESSAGE +import com.reown.sign.common.exceptions.UNAUTHORIZED_METHOD_MESSAGE +import com.reown.android.internal.common.model.Namespace +import com.reown.sign.common.validator.SignValidator +import com.reown.sign.engine.model.EngineDO +import com.reown.sign.engine.model.mapper.toAbsoluteString +import com.reown.sign.validator.ValidatorTest.Accounts.COSMOSHUB_4_1 +import com.reown.sign.validator.ValidatorTest.Accounts.ETHEREUM_1 +import com.reown.sign.validator.ValidatorTest.Accounts.ETHEREUM_2 +import com.reown.sign.validator.ValidatorTest.Accounts.ETHEREUM_3 +import com.reown.sign.validator.ValidatorTest.Accounts.ETHEREUM_4 +import com.reown.sign.validator.ValidatorTest.Accounts.ETHEREUM_5 +import com.reown.sign.validator.ValidatorTest.Accounts.KOVAN_1 +import com.reown.sign.validator.ValidatorTest.Accounts.MATIC_1 +import com.reown.sign.validator.ValidatorTest.Chains.COSMOSHUB_4 +import com.reown.sign.validator.ValidatorTest.Chains.ETHEREUM +import com.reown.sign.validator.ValidatorTest.Chains.KOVAN +import com.reown.sign.validator.ValidatorTest.Chains.MATIC +import com.reown.sign.validator.ValidatorTest.Chains.OPTIMISM +import com.reown.sign.validator.ValidatorTest.Events.ACCOUNTS_CHANGED +import com.reown.sign.validator.ValidatorTest.Events.CHAIN_CHANGED +import com.reown.sign.validator.ValidatorTest.Events.COSMOS_EVENT +import com.reown.sign.validator.ValidatorTest.Events.SOME_EVENT +import com.reown.sign.validator.ValidatorTest.Methods.COSMOS_GET_ACCOUNTS +import com.reown.sign.validator.ValidatorTest.Methods.COSMOS_SIGNDIRECT +import com.reown.sign.validator.ValidatorTest.Methods.ETH_SIGN +import com.reown.sign.validator.ValidatorTest.Methods.PERSONAL_SIGN +import com.reown.sign.validator.ValidatorTest.Namespaces.COSMOS +import com.reown.sign.validator.ValidatorTest.Namespaces.EIP155 +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNotNull +import junit.framework.TestCase.assertNull +import junit.framework.TestCase.assertTrue +import org.junit.Test + +class ValidatorTest { + + private object Namespaces { + const val COSMOS = "cosmos" + const val EIP155 = "eip155" + } + + private object Chains { + const val ETHEREUM = "eip155:1" + const val OPTIMISM = "eip155:10" + const val MATIC = "eip155:137" + const val COSMOSHUB_4 = "cosmos:cosmoshub-4" + const val KOVAN = "eip155:42" + } + + private object Accounts { + const val ETHEREUM_1 = "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" + const val ETHEREUM_2 = "eip155:1:0x25caCa7f7Bf3A77b1738A8c98A666dd9e4C69A0C" + const val ETHEREUM_3 = "eip155:1:0x2Fe1cC9b1DCe6E8e16C48bc6A7ABbAB3d10DA954" + const val ETHEREUM_4 = "eip155:1:0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8" + const val ETHEREUM_5 = "eip155:1:0xEB2F31B0224222D774541BfF89A221e7eb15a17E" + const val KOVAN_1 = "eip155:42:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" + const val MATIC_1 = "eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" + const val COSMOSHUB_4_1 = "cosmos:cosmoshub-4:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0" + } + + private object Methods { + const val COSMOS_SIGNDIRECT = "cosmos_signDirect" + const val COSMOS_GET_ACCOUNTS = "cosmos_getAccounts" + const val ETH_SIGN = "eth_sign" + const val ETH_GET_ACCOUNTS = "eth_getAccounts" + const val PERSONAL_SIGN = "personalSign" + } + + private object Events { + const val COSMOS_EVENT = "someCosmosEvent" + const val ACCOUNTS_CHANGED = "accountsChanged" + const val CHAIN_CHANGED = "chainChanged" + const val SOME_EVENT = "someEvent" + } + + @Test + fun `Convert map to string and decode from string to map test`() { + val map1 = mapOf("1" to "a", "2" to "b") + val stringMap = map1.entries.joinToString() + + val map2 = stringMap.split(",").associate { entry -> + val entries = entry.split("=") + entries.first().trim() to entries.last().trim() + } + + assertEquals(map1, map2) + } + + @Test + fun `Proposal namespaces MAY be empty`() { + val namespaces = emptyMap() + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { error -> errorMessage = error.message } + assertNull(errorMessage) + } + + @Test + fun `Proposal namespaces MAY have empty events`() { + val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf())) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { error -> errorMessage = error.message } + assertNull(errorMessage) + } + + @Test + fun `Proposal Namespaces MUST NOT have chains empty when index as a valid namespace`() { + val namespaces = + mapOf(COSMOS to Namespace.Proposal(chains = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_CHAINS_MISSING_MESSAGE, errorMessage) + } + + @Test + fun `Proposal Namespaces MAY have chains empty when index is caip-2 compatible`() { + val namespaces = + mapOf(COSMOSHUB_4 to Namespace.Proposal(chains = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Proposal Namespaces chains MUST be CAIP-2 compliant when index is namespace compatible`() { + val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf("42"), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED))) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_CHAINS_CAIP_2_MESSAGE, errorMessage) + } + + @Test + fun `Proposal Namespaces methods and events MAY be empty`() { + val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = emptyList(), events = emptyList())) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Proposal Namespaces methods and events MAY be empty when index is caip-2 compatible`() { + val namespaces = mapOf(ETHEREUM to Namespace.Proposal(methods = emptyList(), events = emptyList())) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `All chains in the namespace MUST contain the namespace prefix`() { + val namespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM, COSMOSHUB_4), + methods = listOf(PERSONAL_SIGN), + events = listOf(CHAIN_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Namespace key MUST have chains defined`() { + val namespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = null, + methods = listOf(PERSONAL_SIGN), + events = listOf(CHAIN_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE, errorMessage) + } + + @Test + fun `Namespace key must comply with CAIP-2 specification`() { + val namespaces = mapOf( + "" to Namespace.Proposal(chains = listOf(":1"), methods = listOf(PERSONAL_SIGN), events = emptyList()), + "**" to Namespace.Proposal(chains = listOf("**:1"), methods = listOf(PERSONAL_SIGN), events = emptyList()) + ) + var errorMessage: String? = null + SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_KEYS_INVALID_FORMAT, errorMessage) + } + + @Test + fun `Session Namespaces MAY include anything when when Required Namespaces are empty`() { + val requiredNamespaces = emptyMap() + + val sessionNamespaces = mapOf( + COSMOS to Namespace.Session( + accounts = listOf(COSMOSHUB_4_1), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), chains = listOf(COSMOSHUB_4) + ), + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MUST not be empty`() { + val requiredNamespaces = emptyMap() + val sessionNamespaces = emptyMap() + var errorMessage: String? = null + SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(errorMessage, EMPTY_NAMESPACES_MESSAGE) + } + + @Test + fun `Session Namespaces MAY have accounts empty`() { + val requiredNamespaces = + mapOf(COSMOS to Namespace.Proposal(chains = listOf(COSMOSHUB_4), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) + val sessionNamespaces = mapOf( + COSMOS to Namespace.Session( + accounts = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), chains = listOf(COSMOSHUB_4) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY have accounts empty when index is caip-2 compatible`() { + val requiredNamespaces = + mapOf(COSMOSHUB_4 to Namespace.Proposal(methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) + val sessionNamespaces = mapOf( + COSMOSHUB_4 to Namespace.Session( + accounts = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces addresses MUST be CAIP-10 compliant`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf("eip155:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MUST approve all methods`() { + val requiredNamespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = emptyList())) + + val namespaces = mapOf( + EIP155 to Namespace.Session(accounts = listOf(ETHEREUM_1), methods = emptyList(), events = emptyList(), chains = listOf(ETHEREUM)) + ) + + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_METHODS_MISSING_MESSAGE, errorMessage) + } + + @Test + fun `chains MAY be null`() { + val requiredNamespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = emptyList())) + val namespaces = mapOf(EIP155 to Namespace.Session(accounts = listOf(ETHEREUM_1), methods = listOf(ETH_SIGN), events = emptyList())) + + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MUST approve all events`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = emptyList(), events = listOf(CHAIN_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), methods = emptyList(), events = emptyList(), chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_EVENTS_MISSING_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MAY NOT contain at least one account in requested chains`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = emptyList(), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY contain multiple accounts for one chain`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, ETHEREUM_5), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MUAST contain accounts conforming chains`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, KOVAN_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MAY contain multiple accounts for one chain when index is caip-2 compatible`() { + val requiredNamespaces = mapOf( + ETHEREUM to Namespace.Proposal( + methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + + val namespaces = mapOf( + ETHEREUM to Namespace.Session( + accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, ETHEREUM_5), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MUAST contain only accounts conforming the chain`() { + val requiredNamespaces = mapOf( + ETHEREUM to Namespace.Proposal( + methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + ETHEREUM to Namespace.Session( + accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, COSMOSHUB_4_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MAY extend methods and events of Required Namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN, PERSONAL_SIGN), + events = listOf(ACCOUNTS_CHANGED, SOME_EVENT), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces key MUST BE CAIP-2 compatible when chains are not included`() { + val requiredNamespaces = mapOf( + ETHEREUM to Namespace.Proposal( + methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + ETHEREUM to Namespace.Session( + accounts = emptyList(), + methods = listOf(ETH_SIGN, PERSONAL_SIGN), + events = listOf(ACCOUNTS_CHANGED, SOME_EVENT), + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `All accounts in the namespace MUST contain the namespace prefix`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, COSMOSHUB_4_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Once required namespaces are satisfied anything can be added on top even if not included in optional namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ), + "cosmos" to Namespace.Session( + methods = listOf("cosmos_method"), + events = listOf("cosmos_event"), + chains = listOf("cosmos:cosmosHUB"), + accounts = emptyList() + ) + + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MUST contain accounts on chains defined in chains in Proposal namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, KOVAN_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MUST contain accounts on chains defined in chain index on Proposal namespaces`() { + val requiredNamespaces = mapOf( + ETHEREUM to Namespace.Proposal( + methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + ETHEREUM to Namespace.Session( + accounts = listOf(ETHEREUM_1, KOVAN_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MUST have all the same namespaces as the Required Namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ), + COSMOS to Namespace.Proposal( + chains = listOf(COSMOSHUB_4), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(NAMESPACE_KEYS_MISSING_MESSAGE, errorMessage) + } + + @Test + fun `Session Namespaces MAY have empty account list`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Optional MAY be merged into namespace`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN, PERSONAL_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY extend methods and events and chains of Proposal Namespace`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = emptyList(), events = listOf(CHAIN_CHANGED), + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC, KOVAN) + ) + ) + + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces includes apporved chain indexing`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal(chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(CHAIN_CHANGED)) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, KOVAN) + ), + MATIC to Namespace.Session( + accounts = emptyList(), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED) + ) + ) + + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Any additional namespace in the Session namespaces on top of the required namespaces MAY NOT be included in optional namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), + ) + ) + + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ), + COSMOS to Namespace.Session( + methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), accounts = emptyList(), chains = listOf(COSMOSHUB_4) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY add namespaces not defined in optional namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ), + COSMOS to Namespace.Session( + methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), accounts = emptyList(), chains = listOf(COSMOSHUB_4) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY NOT include optional namespaces`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(MATIC, ETHEREUM) + ), + + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY include optional namespaces with arbitrary methods`() { + val requiredNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) + ) + ) + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1, MATIC_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM, MATIC) + ), + COSMOS to Namespace.Session( + methods = emptyList(), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY include one optional namespaces when required are empty`() { + val requiredNamespaces = emptyMap() + + val namespaces = mapOf( + COSMOS to Namespace.Session( + methods = listOf(COSMOS_GET_ACCOUNTS), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY include more namespaces than optional when required are empty`() { + val requiredNamespaces = emptyMap() + val optionalNamespaces = mapOf( + EIP155 to Namespace.Proposal( + chains = listOf(ETHEREUM), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED) + ), + ) + + val namespaces = mapOf( + COSMOS to Namespace.Session( + methods = listOf(COSMOS_GET_ACCOUNTS), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) + ), + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + chains = listOf(ETHEREUM) + ), + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Session Namespaces MAY omit optional namespaces when required are empty`() { + val requiredNamespaces = emptyMap() + val namespaces = mapOf( + EIP155 to Namespace.Session( + chains = listOf(ETHEREUM), + methods = listOf(ETH_SIGN), + events = listOf(ACCOUNTS_CHANGED), + accounts = emptyList() + ) + ) + var errorMessage: String? = null + SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Authorizing not approved event`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithEventAuthorisation(ETHEREUM, ACCOUNTS_CHANGED, namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(UNAUTHORIZED_EVENT_MESSAGE, errorMessage) + } + + @Test + fun `Authorizing not approved method`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED), + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, PERSONAL_SIGN, namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(UNAUTHORIZED_METHOD_MESSAGE, errorMessage) + } + + @Test + fun `Authorizing somewhere approved event but not by requested chain`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED), + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithEventAuthorisation(OPTIMISM, CHAIN_CHANGED, namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(UNAUTHORIZED_EVENT_MESSAGE, errorMessage) + } + + @Test + fun `Authorizing somewhere approved method but not by requested chain`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithMethodAuthorisation(OPTIMISM, ETH_SIGN, namespaces) { errorMessage = it.message } + assertNotNull(errorMessage) + assertEquals(UNAUTHORIZED_METHOD_MESSAGE, errorMessage) + } + + @Test + fun `Authorizing approved method where namespace index is chainId`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + chains = listOf(ETHEREUM), + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN, "test"), + events = listOf(CHAIN_CHANGED) + ), + KOVAN to Namespace.Session( + accounts = listOf(KOVAN_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED) + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, "test", namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Authorizing approved event`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithEventAuthorisation(ETHEREUM, CHAIN_CHANGED, namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Authorizing approved events where namespace index is chainId`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + chains = listOf(ETHEREUM), + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED) + ), + KOVAN to Namespace.Session( + accounts = listOf(KOVAN_1), + methods = listOf(ETH_SIGN), + events = listOf("test") + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithEventAuthorisation(KOVAN, "test", namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `Authorizing approved method`() { + val namespaces = mapOf( + EIP155 to Namespace.Session( + accounts = listOf(ETHEREUM_1), + methods = listOf(ETH_SIGN), + events = listOf(CHAIN_CHANGED), + chains = listOf(ETHEREUM) + ) + ) + var errorMessage: String? = null + SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, ETH_SIGN, namespaces) { errorMessage = it.message } + assertNull(errorMessage) + } + + @Test + fun `is event valid test`() { + var event = EngineDO.Event("", "data", ETHEREUM) + var errorMessage: String? = null + SignValidator.validateEvent(event) { + errorMessage = it.message + } + assertEquals(INVALID_EVENT_MESSAGE, errorMessage) + + + event = EngineDO.Event("someName", "", ETHEREUM) + errorMessage = null + SignValidator.validateEvent(event) { + errorMessage = it.message + } + assertEquals(INVALID_EVENT_MESSAGE, errorMessage) + + event = EngineDO.Event("someName", "someData", "") + errorMessage = null + SignValidator.validateEvent(event) { + errorMessage = it.message + } + assertEquals(INVALID_EVENT_MESSAGE, errorMessage) + + event = EngineDO.Event("someName", "someData", "1") + errorMessage = null + SignValidator.validateEvent(event) { + errorMessage = it.message + } + assertEquals(INVALID_EVENT_MESSAGE, errorMessage) + } + + @Test + fun `is request valid test`() { + var request = EngineDO.Request("", "someMethod", "someParams", ETHEREUM) + var errorMessage: String? = null + SignValidator.validateSessionRequest(request) { + errorMessage = it.message + } + assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) + + request = EngineDO.Request("someTopic", "", "someParams", ETHEREUM) + errorMessage = null + SignValidator.validateSessionRequest(request) { + errorMessage = it.message + } + assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) + + request = EngineDO.Request("someTopic", "someMethod", "", ETHEREUM) + errorMessage = null + SignValidator.validateSessionRequest(request) { + errorMessage = it.message + } + assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) + + request = EngineDO.Request("someTopic", "someMethod", "someParams", "") + errorMessage = null + SignValidator.validateSessionRequest(request) { + errorMessage = it.message + } + assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) + + request = EngineDO.Request("someTopic", "someMethod", "someParams", "1") + errorMessage = null + SignValidator.validateSessionRequest(request) { + errorMessage = it.message + } + assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) + } + + @Test + fun `validate WC uri test`() { + val validUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + + SignValidator.validateWCUri("").apply { assertEquals(null, this) } + SignValidator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + } + + val noTopicInvalidUri = + "wc:@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + SignValidator.validateWCUri(noTopicInvalidUri).apply { assertNull(this) } + + val noPrefixInvalidUri = + "7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + SignValidator.validateWCUri(noPrefixInvalidUri).apply { assertNull(this) } + + val noSymKeyInvalidUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=" + SignValidator.validateWCUri(noSymKeyInvalidUri).apply { assertNull(this) } + + val noProtocolTypeInvalidUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + SignValidator.validateWCUri(noProtocolTypeInvalidUri).apply { assertNull(this) } + } + + @Test + fun `validate WC uri test optional data field`() { + val validUri = + "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&relay-data=testData&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" + + SignValidator.validateWCUri("").apply { assertEquals(null, this) } + SignValidator.validateWCUri(validUri).apply { + assertNotNull(this) + assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) + assertEquals("irn", this!!.relay.protocol) + assertEquals("testData", this.relay.data) + assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) + assertEquals("2", this.version) + } + } + + @Test + fun `parse walletconnect uri to absolute string`() { + val uri = EngineDO.WalletConnectUri( + Topic("11112222244444"), + SymmetricKey("0x12321321312312312321"), + RelayProtocolOptions("irn", "teeestData") + ) + + assertEquals(uri.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&relay-data=teeestData&symKey=0x12321321312312312321") + + val uri2 = EngineDO.WalletConnectUri( + Topic("11112222244444"), + SymmetricKey("0x12321321312312312321"), + RelayProtocolOptions("irn") + ) + + assertEquals(uri2.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&symKey=0x12321321312312312321") + } + + @Test + fun `extend session expiry less than 1 week`() { + val currentExpiry: Long = 1646641841 //07.03 + val newExpiry: Long = 1646901496 //10.03 + SignValidator.validateSessionExtend(newExpiry, currentExpiry) { + assertTrue(false) + } + } + + @Test + fun `extend session expiry over 1 week`() { + val currentExpiry: Long = 1646641841 //07.03 + val newExpiry: Long = 1647765496 //20.03 + + SignValidator.validateSessionExtend(newExpiry, currentExpiry) { + assertEquals(INVALID_EXTEND_TIME, it.message) + } + } + + @Test + fun `extend session expiry less than current expiry`() { + val currentExpiry: Long = 1646641841 //07.03 + val newExpiry: Long = 1646555896 //06.03 + + SignValidator.validateSessionExtend(newExpiry, currentExpiry) { + assertEquals(INVALID_EXTEND_TIME, it.message) + } + } + + @Test + fun `test time periods in seconds`() { + fiveMinutesInSeconds.apply { assertEquals(this.compareTo(300), 0) } + dayInSeconds.apply { assertEquals(this.compareTo(86400), 0) } + weekInSeconds.apply { assertEquals(this.compareTo(604800), 0) } + monthInSeconds.apply { assertEquals(this.compareTo(2592000), 0) } + } +} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt deleted file mode 100644 index 2aaf2e542..000000000 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/ProposeSessionUseCaseTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.crypto.kmr.KeyManagementRepository -import com.walletconnect.android.internal.common.model.AppMetaData -import com.walletconnect.android.internal.common.model.Expiry -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.common.exceptions.InvalidNamespaceException -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.proposal.ProposalStorageRepository -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertSame -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Test - -class ProposeSessionUseCaseTest { - private val jsonRpcInteractor = mockk() - private val crypto = mockk() - private val proposalStorageRepository = mockk() - private val selfAppMetaData = mockk() - private val logger = mockk() - private val proposeSessionUseCase = ProposeSessionUseCase(jsonRpcInteractor, crypto, proposalStorageRepository, selfAppMetaData, logger) - - @Before - fun setUp() { - every { logger.error(any() as String) } answers { } - every { logger.error(any() as Exception) } answers { } - } - - @Test - fun `onFailure is called when SignValidator validateProposalNamespaces fails`() = runTest { - - proposeSessionUseCase.proposeSession( - requiredNamespaces = mapOf("required" to EngineDO.Namespace.Proposal(listOf("required"), listOf("required"), listOf("required"))), - optionalNamespaces = mapOf("optional" to EngineDO.Namespace.Proposal(listOf("optional"), listOf("optional"), listOf("optional"))), - properties = mapOf("key" to "value"), - pairing = com.walletconnect.android.internal.common.model.Pairing( - topic = Topic("topic"), - relay = RelayProtocolOptions(), - symmetricKey = SymmetricKey("symmetricKey"), - methods = "", - expiry = Expiry(12345678L) - ), - onSuccess = { - fail("onSuccess should not be called since should have validation failed") - }, - onFailure = { error -> - assertSame(InvalidNamespaceException::class, error::class) - } - ) - } -} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt deleted file mode 100644 index 249b506bb..000000000 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/RespondSessionRequestUseCaseTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.JsonRpcResponse -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.internal.common.storage.verify.VerifyContextStorageRepository -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.json_rpc.domain.GetPendingJsonRpcHistoryEntryByIdUseCase -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Before -import org.junit.Test - -class RespondSessionRequestUseCaseTest { - private val jsonRpcInteractor = mockk() - private val sessionStorageRepository = mockk() - private val getPendingJsonRpcHistoryEntryByIdUseCase = mockk() - private val logger = mockk() - private val verifyContextStorageRepository = mockk() - private val metadataStorageRepository = mockk() - private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface = mockk() - private val insertEventUseCase = mockk() - private val respondSessionRequestUseCase = RespondSessionRequestUseCase( - jsonRpcInteractor, - sessionStorageRepository, - getPendingJsonRpcHistoryEntryByIdUseCase, - linkModeJsonRpcInteractor, - logger, - verifyContextStorageRepository, - metadataStorageRepository, - insertEventUseCase, - "clientId" - ) - - @Before - fun setUp() { - every { logger.error(any() as String) } answers { } - every { logger.error(any() as Exception) } answers { } - } - - @Test - fun `onFailure is called when sessionStorageRepository isSessionValid is false`() = runTest { - every { sessionStorageRepository.isSessionValid(any()) } returns false - - respondSessionRequestUseCase.respondSessionRequest( - topic = "topic", - jsonRpcResponse = JsonRpcResponse.JsonRpcResult(1, "2.0", "result"), - onSuccess = { - Assert.fail("onSuccess should not be called since should have validation failed") - }, - onFailure = { error -> - Assert.assertSame(CannotFindSequenceForTopic::class, error::class) - } - ) - } -} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt b/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt deleted file mode 100644 index 75ccc19e9..000000000 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/engine/use_case/calls/SessionRequestUseCaseTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.walletconnect.sign.engine.use_case.calls - -import com.walletconnect.android.internal.common.exception.CannotFindSequenceForTopic -import com.walletconnect.android.internal.common.json_rpc.domain.link_mode.LinkModeJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.model.type.RelayJsonRpcInteractorInterface -import com.walletconnect.android.internal.common.storage.metadata.MetadataStorageRepositoryInterface -import com.walletconnect.android.pulse.domain.InsertEventUseCase -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.storage.sequence.SessionStorageRepository -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertSame -import org.junit.Assert.fail -import org.junit.Before -import org.junit.Test - -class SessionRequestUseCaseTest { - private val sessionStorageRepository = mockk() - private val jsonRpcInteractor = mockk() - private val logger = mockk() - private val linkModeJsonRpcInteractor: LinkModeJsonRpcInteractorInterface = mockk() - private val metadataStorageRepository = mockk() - private val insertEventUseCase = mockk() - private val sessionRequestUseCase = SessionRequestUseCase( - sessionStorageRepository, - jsonRpcInteractor, - linkModeJsonRpcInteractor, - metadataStorageRepository, - insertEventUseCase, - "clientId", - logger - ) - - @Before - fun setUp() { - every { logger.error(any() as String) } answers { } - every { logger.error(any() as Exception) } answers { } - } - - @Test - fun `onFailure is called when sessionStorageRepository isSessionValid is false`() = runTest { - every { sessionStorageRepository.isSessionValid(any()) } returns false - - sessionRequestUseCase.sessionRequest( - request = EngineDO.Request( - topic = "topic", - method = "method", - params = "params", - chainId = "chainId", - ), - onSuccess = { - fail("onSuccess should not be called since should have validation failed") - }, - onFailure = { error -> - assertSame(CannotFindSequenceForTopic::class, error::class) - } - ) - } -} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/UtilFunctions.kt b/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/UtilFunctions.kt deleted file mode 100644 index 51591039c..000000000 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/util/UtilFunctions.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.walletconnect.sign.util - -import junit.framework.TestCase -import org.json.JSONArray -import org.json.JSONObject -internal fun iterateJsonArrays(expJsonArray: JSONArray, actJsonArray: JSONArray) { - TestCase.assertEquals(expJsonArray.length(), actJsonArray.length()) - - (0 until expJsonArray.length()).forEach { index -> - val expCurrentIndexItem = expJsonArray.get(index) - val actCurrentIndexItem = actJsonArray[index] - - when { - expCurrentIndexItem is JSONObject && actCurrentIndexItem is JSONObject -> iterateJsonObjects(expCurrentIndexItem, actCurrentIndexItem) - expCurrentIndexItem is JSONArray && actCurrentIndexItem is JSONArray -> iterateJsonArrays(expCurrentIndexItem, actCurrentIndexItem) - else -> TestCase.assertEquals(expCurrentIndexItem, actCurrentIndexItem) - } - } -} - -internal fun iterateJsonObjects(expJsonObject: JSONObject, actJsonObject: JSONObject) { - expJsonObject.keys().forEach { key -> - assert(actJsonObject.has(key)) - val expCurrentItem = expJsonObject.get(key) - val actCurrentItem = actJsonObject.get(key) - - when { - expCurrentItem is JSONObject && actCurrentItem is JSONObject -> { - iterateJsonObjects(expCurrentItem, actCurrentItem) - } - - expCurrentItem is JSONArray && actCurrentItem is JSONArray -> { - iterateJsonArrays(expCurrentItem, actCurrentItem) - } - - else -> TestCase.assertEquals(expCurrentItem, actCurrentItem) - } - } -} \ No newline at end of file diff --git a/protocol/sign/src/test/kotlin/com/walletconnect/sign/validator/ValidatorTest.kt b/protocol/sign/src/test/kotlin/com/walletconnect/sign/validator/ValidatorTest.kt deleted file mode 100644 index b71ce9e16..000000000 --- a/protocol/sign/src/test/kotlin/com/walletconnect/sign/validator/ValidatorTest.kt +++ /dev/null @@ -1,1124 +0,0 @@ -package com.walletconnect.sign.validator - -import com.walletconnect.android.internal.common.model.RelayProtocolOptions -import com.walletconnect.android.internal.common.model.SymmetricKey -import com.walletconnect.android.internal.utils.dayInSeconds -import com.walletconnect.android.internal.utils.fiveMinutesInSeconds -import com.walletconnect.android.internal.utils.monthInSeconds -import com.walletconnect.android.internal.utils.weekInSeconds -import com.walletconnect.foundation.common.model.Topic -import com.walletconnect.sign.common.exceptions.EMPTY_NAMESPACES_MESSAGE -import com.walletconnect.sign.common.exceptions.INVALID_EVENT_MESSAGE -import com.walletconnect.sign.common.exceptions.INVALID_EXTEND_TIME -import com.walletconnect.sign.common.exceptions.INVALID_REQUEST_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_CAIP_2_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_EVENTS_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_KEYS_INVALID_FORMAT -import com.walletconnect.sign.common.exceptions.NAMESPACE_KEYS_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.NAMESPACE_METHODS_MISSING_MESSAGE -import com.walletconnect.sign.common.exceptions.UNAUTHORIZED_EVENT_MESSAGE -import com.walletconnect.sign.common.exceptions.UNAUTHORIZED_METHOD_MESSAGE -import com.walletconnect.android.internal.common.model.Namespace -import com.walletconnect.sign.common.validator.SignValidator -import com.walletconnect.sign.engine.model.EngineDO -import com.walletconnect.sign.engine.model.mapper.toAbsoluteString -import com.walletconnect.sign.validator.ValidatorTest.Accounts.COSMOSHUB_4_1 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.ETHEREUM_1 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.ETHEREUM_2 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.ETHEREUM_3 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.ETHEREUM_4 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.ETHEREUM_5 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.KOVAN_1 -import com.walletconnect.sign.validator.ValidatorTest.Accounts.MATIC_1 -import com.walletconnect.sign.validator.ValidatorTest.Chains.COSMOSHUB_4 -import com.walletconnect.sign.validator.ValidatorTest.Chains.ETHEREUM -import com.walletconnect.sign.validator.ValidatorTest.Chains.KOVAN -import com.walletconnect.sign.validator.ValidatorTest.Chains.MATIC -import com.walletconnect.sign.validator.ValidatorTest.Chains.OPTIMISM -import com.walletconnect.sign.validator.ValidatorTest.Events.ACCOUNTS_CHANGED -import com.walletconnect.sign.validator.ValidatorTest.Events.CHAIN_CHANGED -import com.walletconnect.sign.validator.ValidatorTest.Events.COSMOS_EVENT -import com.walletconnect.sign.validator.ValidatorTest.Events.SOME_EVENT -import com.walletconnect.sign.validator.ValidatorTest.Methods.COSMOS_GET_ACCOUNTS -import com.walletconnect.sign.validator.ValidatorTest.Methods.COSMOS_SIGNDIRECT -import com.walletconnect.sign.validator.ValidatorTest.Methods.ETH_SIGN -import com.walletconnect.sign.validator.ValidatorTest.Methods.PERSONAL_SIGN -import com.walletconnect.sign.validator.ValidatorTest.Namespaces.COSMOS -import com.walletconnect.sign.validator.ValidatorTest.Namespaces.EIP155 -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertNotNull -import junit.framework.TestCase.assertNull -import junit.framework.TestCase.assertTrue -import org.junit.Test - -class ValidatorTest { - - private object Namespaces { - const val COSMOS = "cosmos" - const val EIP155 = "eip155" - } - - private object Chains { - const val ETHEREUM = "eip155:1" - const val OPTIMISM = "eip155:10" - const val MATIC = "eip155:137" - const val COSMOSHUB_4 = "cosmos:cosmoshub-4" - const val KOVAN = "eip155:42" - } - - private object Accounts { - const val ETHEREUM_1 = "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" - const val ETHEREUM_2 = "eip155:1:0x25caCa7f7Bf3A77b1738A8c98A666dd9e4C69A0C" - const val ETHEREUM_3 = "eip155:1:0x2Fe1cC9b1DCe6E8e16C48bc6A7ABbAB3d10DA954" - const val ETHEREUM_4 = "eip155:1:0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8" - const val ETHEREUM_5 = "eip155:1:0xEB2F31B0224222D774541BfF89A221e7eb15a17E" - const val KOVAN_1 = "eip155:42:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" - const val MATIC_1 = "eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" - const val COSMOSHUB_4_1 = "cosmos:cosmoshub-4:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0" - } - - private object Methods { - const val COSMOS_SIGNDIRECT = "cosmos_signDirect" - const val COSMOS_GET_ACCOUNTS = "cosmos_getAccounts" - const val ETH_SIGN = "eth_sign" - const val ETH_GET_ACCOUNTS = "eth_getAccounts" - const val PERSONAL_SIGN = "personalSign" - } - - private object Events { - const val COSMOS_EVENT = "someCosmosEvent" - const val ACCOUNTS_CHANGED = "accountsChanged" - const val CHAIN_CHANGED = "chainChanged" - const val SOME_EVENT = "someEvent" - } - - @Test - fun `Convert map to string and decode from string to map test`() { - val map1 = mapOf("1" to "a", "2" to "b") - val stringMap = map1.entries.joinToString() - - val map2 = stringMap.split(",").associate { entry -> - val entries = entry.split("=") - entries.first().trim() to entries.last().trim() - } - - assertEquals(map1, map2) - } - - @Test - fun `Proposal namespaces MAY be empty`() { - val namespaces = emptyMap() - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { error -> errorMessage = error.message } - assertNull(errorMessage) - } - - @Test - fun `Proposal namespaces MAY have empty events`() { - val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf())) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { error -> errorMessage = error.message } - assertNull(errorMessage) - } - - @Test - fun `Proposal Namespaces MUST NOT have chains empty when index as a valid namespace`() { - val namespaces = - mapOf(COSMOS to Namespace.Proposal(chains = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_CHAINS_MISSING_MESSAGE, errorMessage) - } - - @Test - fun `Proposal Namespaces MAY have chains empty when index is caip-2 compatible`() { - val namespaces = - mapOf(COSMOSHUB_4 to Namespace.Proposal(chains = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Proposal Namespaces chains MUST be CAIP-2 compliant when index is namespace compatible`() { - val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf("42"), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED))) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_CHAINS_CAIP_2_MESSAGE, errorMessage) - } - - @Test - fun `Proposal Namespaces methods and events MAY be empty`() { - val namespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = emptyList(), events = emptyList())) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Proposal Namespaces methods and events MAY be empty when index is caip-2 compatible`() { - val namespaces = mapOf(ETHEREUM to Namespace.Proposal(methods = emptyList(), events = emptyList())) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `All chains in the namespace MUST contain the namespace prefix`() { - val namespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM, COSMOSHUB_4), - methods = listOf(PERSONAL_SIGN), - events = listOf(CHAIN_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_CHAINS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Namespace key MUST have chains defined`() { - val namespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = null, - methods = listOf(PERSONAL_SIGN), - events = listOf(CHAIN_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_CHAINS_UNDEFINED_MISSING_MESSAGE, errorMessage) - } - - @Test - fun `Namespace key must comply with CAIP-2 specification`() { - val namespaces = mapOf( - "" to Namespace.Proposal(chains = listOf(":1"), methods = listOf(PERSONAL_SIGN), events = emptyList()), - "**" to Namespace.Proposal(chains = listOf("**:1"), methods = listOf(PERSONAL_SIGN), events = emptyList()) - ) - var errorMessage: String? = null - SignValidator.validateProposalNamespaces(namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_KEYS_INVALID_FORMAT, errorMessage) - } - - @Test - fun `Session Namespaces MAY include anything when when Required Namespaces are empty`() { - val requiredNamespaces = emptyMap() - - val sessionNamespaces = mapOf( - COSMOS to Namespace.Session( - accounts = listOf(COSMOSHUB_4_1), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), chains = listOf(COSMOSHUB_4) - ), - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MUST not be empty`() { - val requiredNamespaces = emptyMap() - val sessionNamespaces = emptyMap() - var errorMessage: String? = null - SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(errorMessage, EMPTY_NAMESPACES_MESSAGE) - } - - @Test - fun `Session Namespaces MAY have accounts empty`() { - val requiredNamespaces = - mapOf(COSMOS to Namespace.Proposal(chains = listOf(COSMOSHUB_4), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) - val sessionNamespaces = mapOf( - COSMOS to Namespace.Session( - accounts = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), chains = listOf(COSMOSHUB_4) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY have accounts empty when index is caip-2 compatible`() { - val requiredNamespaces = - mapOf(COSMOSHUB_4 to Namespace.Proposal(methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT))) - val sessionNamespaces = mapOf( - COSMOSHUB_4 to Namespace.Session( - accounts = emptyList(), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(sessionNamespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces addresses MUST be CAIP-10 compliant`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf("eip155:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_CAIP_10_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MUST approve all methods`() { - val requiredNamespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = emptyList())) - - val namespaces = mapOf( - EIP155 to Namespace.Session(accounts = listOf(ETHEREUM_1), methods = emptyList(), events = emptyList(), chains = listOf(ETHEREUM)) - ) - - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_METHODS_MISSING_MESSAGE, errorMessage) - } - - @Test - fun `chains MAY be null`() { - val requiredNamespaces = mapOf(EIP155 to Namespace.Proposal(chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = emptyList())) - val namespaces = mapOf(EIP155 to Namespace.Session(accounts = listOf(ETHEREUM_1), methods = listOf(ETH_SIGN), events = emptyList())) - - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MUST approve all events`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = emptyList(), events = listOf(CHAIN_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), methods = emptyList(), events = emptyList(), chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_EVENTS_MISSING_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MAY NOT contain at least one account in requested chains`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = emptyList(), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY contain multiple accounts for one chain`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, ETHEREUM_5), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MUAST contain accounts conforming chains`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, KOVAN_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MAY contain multiple accounts for one chain when index is caip-2 compatible`() { - val requiredNamespaces = mapOf( - ETHEREUM to Namespace.Proposal( - methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - - val namespaces = mapOf( - ETHEREUM to Namespace.Session( - accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, ETHEREUM_5), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MUAST contain only accounts conforming the chain`() { - val requiredNamespaces = mapOf( - ETHEREUM to Namespace.Proposal( - methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - ETHEREUM to Namespace.Session( - accounts = listOf(ETHEREUM_1, ETHEREUM_2, ETHEREUM_3, ETHEREUM_4, COSMOSHUB_4_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MAY extend methods and events of Required Namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN, PERSONAL_SIGN), - events = listOf(ACCOUNTS_CHANGED, SOME_EVENT), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces key MUST BE CAIP-2 compatible when chains are not included`() { - val requiredNamespaces = mapOf( - ETHEREUM to Namespace.Proposal( - methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - ETHEREUM to Namespace.Session( - accounts = emptyList(), - methods = listOf(ETH_SIGN, PERSONAL_SIGN), - events = listOf(ACCOUNTS_CHANGED, SOME_EVENT), - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `All accounts in the namespace MUST contain the namespace prefix`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, COSMOSHUB_4_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Once required namespaces are satisfied anything can be added on top even if not included in optional namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ), - "cosmos" to Namespace.Session( - methods = listOf("cosmos_method"), - events = listOf("cosmos_event"), - chains = listOf("cosmos:cosmosHUB"), - accounts = emptyList() - ) - - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MUST contain accounts on chains defined in chains in Proposal namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, KOVAN_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MUST contain accounts on chains defined in chain index on Proposal namespaces`() { - val requiredNamespaces = mapOf( - ETHEREUM to Namespace.Proposal( - methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - ETHEREUM to Namespace.Session( - accounts = listOf(ETHEREUM_1, KOVAN_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_ACCOUNTS_WRONG_NAMESPACE_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MUST have all the same namespaces as the Required Namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ), - COSMOS to Namespace.Proposal( - chains = listOf(COSMOSHUB_4), methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(NAMESPACE_KEYS_MISSING_MESSAGE, errorMessage) - } - - @Test - fun `Session Namespaces MAY have empty account list`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Optional MAY be merged into namespace`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN, PERSONAL_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY extend methods and events and chains of Proposal Namespace`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = emptyList(), events = listOf(CHAIN_CHANGED), - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC, KOVAN) - ) - ) - - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces includes apporved chain indexing`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal(chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(CHAIN_CHANGED)) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, KOVAN) - ), - MATIC to Namespace.Session( - accounts = emptyList(), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED, ACCOUNTS_CHANGED) - ) - ) - - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Any additional namespace in the Session namespaces on top of the required namespaces MAY NOT be included in optional namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED), - ) - ) - - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ), - COSMOS to Namespace.Session( - methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), accounts = emptyList(), chains = listOf(COSMOSHUB_4) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY add namespaces not defined in optional namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ), - COSMOS to Namespace.Session( - methods = listOf(COSMOS_SIGNDIRECT), events = listOf(COSMOS_EVENT), accounts = emptyList(), chains = listOf(COSMOSHUB_4) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY NOT include optional namespaces`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(MATIC, ETHEREUM) - ), - - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY include optional namespaces with arbitrary methods`() { - val requiredNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(MATIC, ETHEREUM), methods = listOf(ETH_SIGN), events = listOf(ACCOUNTS_CHANGED) - ) - ) - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1, MATIC_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM, MATIC) - ), - COSMOS to Namespace.Session( - methods = emptyList(), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY include one optional namespaces when required are empty`() { - val requiredNamespaces = emptyMap() - - val namespaces = mapOf( - COSMOS to Namespace.Session( - methods = listOf(COSMOS_GET_ACCOUNTS), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY include more namespaces than optional when required are empty`() { - val requiredNamespaces = emptyMap() - val optionalNamespaces = mapOf( - EIP155 to Namespace.Proposal( - chains = listOf(ETHEREUM), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED) - ), - ) - - val namespaces = mapOf( - COSMOS to Namespace.Session( - methods = listOf(COSMOS_GET_ACCOUNTS), events = listOf(COSMOS_EVENT), accounts = listOf(COSMOSHUB_4_1), chains = listOf(COSMOSHUB_4) - ), - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - chains = listOf(ETHEREUM) - ), - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Session Namespaces MAY omit optional namespaces when required are empty`() { - val requiredNamespaces = emptyMap() - val namespaces = mapOf( - EIP155 to Namespace.Session( - chains = listOf(ETHEREUM), - methods = listOf(ETH_SIGN), - events = listOf(ACCOUNTS_CHANGED), - accounts = emptyList() - ) - ) - var errorMessage: String? = null - SignValidator.validateSessionNamespace(namespaces, requiredNamespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Authorizing not approved event`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithEventAuthorisation(ETHEREUM, ACCOUNTS_CHANGED, namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(UNAUTHORIZED_EVENT_MESSAGE, errorMessage) - } - - @Test - fun `Authorizing not approved method`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED), - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, PERSONAL_SIGN, namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(UNAUTHORIZED_METHOD_MESSAGE, errorMessage) - } - - @Test - fun `Authorizing somewhere approved event but not by requested chain`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED), - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithEventAuthorisation(OPTIMISM, CHAIN_CHANGED, namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(UNAUTHORIZED_EVENT_MESSAGE, errorMessage) - } - - @Test - fun `Authorizing somewhere approved method but not by requested chain`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithMethodAuthorisation(OPTIMISM, ETH_SIGN, namespaces) { errorMessage = it.message } - assertNotNull(errorMessage) - assertEquals(UNAUTHORIZED_METHOD_MESSAGE, errorMessage) - } - - @Test - fun `Authorizing approved method where namespace index is chainId`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - chains = listOf(ETHEREUM), - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN, "test"), - events = listOf(CHAIN_CHANGED) - ), - KOVAN to Namespace.Session( - accounts = listOf(KOVAN_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED) - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, "test", namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Authorizing approved event`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithEventAuthorisation(ETHEREUM, CHAIN_CHANGED, namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Authorizing approved events where namespace index is chainId`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - chains = listOf(ETHEREUM), - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED) - ), - KOVAN to Namespace.Session( - accounts = listOf(KOVAN_1), - methods = listOf(ETH_SIGN), - events = listOf("test") - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithEventAuthorisation(KOVAN, "test", namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `Authorizing approved method`() { - val namespaces = mapOf( - EIP155 to Namespace.Session( - accounts = listOf(ETHEREUM_1), - methods = listOf(ETH_SIGN), - events = listOf(CHAIN_CHANGED), - chains = listOf(ETHEREUM) - ) - ) - var errorMessage: String? = null - SignValidator.validateChainIdWithMethodAuthorisation(ETHEREUM, ETH_SIGN, namespaces) { errorMessage = it.message } - assertNull(errorMessage) - } - - @Test - fun `is event valid test`() { - var event = EngineDO.Event("", "data", ETHEREUM) - var errorMessage: String? = null - SignValidator.validateEvent(event) { - errorMessage = it.message - } - assertEquals(INVALID_EVENT_MESSAGE, errorMessage) - - - event = EngineDO.Event("someName", "", ETHEREUM) - errorMessage = null - SignValidator.validateEvent(event) { - errorMessage = it.message - } - assertEquals(INVALID_EVENT_MESSAGE, errorMessage) - - event = EngineDO.Event("someName", "someData", "") - errorMessage = null - SignValidator.validateEvent(event) { - errorMessage = it.message - } - assertEquals(INVALID_EVENT_MESSAGE, errorMessage) - - event = EngineDO.Event("someName", "someData", "1") - errorMessage = null - SignValidator.validateEvent(event) { - errorMessage = it.message - } - assertEquals(INVALID_EVENT_MESSAGE, errorMessage) - } - - @Test - fun `is request valid test`() { - var request = EngineDO.Request("", "someMethod", "someParams", ETHEREUM) - var errorMessage: String? = null - SignValidator.validateSessionRequest(request) { - errorMessage = it.message - } - assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) - - request = EngineDO.Request("someTopic", "", "someParams", ETHEREUM) - errorMessage = null - SignValidator.validateSessionRequest(request) { - errorMessage = it.message - } - assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) - - request = EngineDO.Request("someTopic", "someMethod", "", ETHEREUM) - errorMessage = null - SignValidator.validateSessionRequest(request) { - errorMessage = it.message - } - assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) - - request = EngineDO.Request("someTopic", "someMethod", "someParams", "") - errorMessage = null - SignValidator.validateSessionRequest(request) { - errorMessage = it.message - } - assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) - - request = EngineDO.Request("someTopic", "someMethod", "someParams", "1") - errorMessage = null - SignValidator.validateSessionRequest(request) { - errorMessage = it.message - } - assertEquals(INVALID_REQUEST_MESSAGE, errorMessage) - } - - @Test - fun `validate WC uri test`() { - val validUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - - SignValidator.validateWCUri("").apply { assertEquals(null, this) } - SignValidator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - } - - val noTopicInvalidUri = - "wc:@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - SignValidator.validateWCUri(noTopicInvalidUri).apply { assertNull(this) } - - val noPrefixInvalidUri = - "7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - SignValidator.validateWCUri(noPrefixInvalidUri).apply { assertNull(this) } - - val noSymKeyInvalidUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=" - SignValidator.validateWCUri(noSymKeyInvalidUri).apply { assertNull(this) } - - val noProtocolTypeInvalidUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - SignValidator.validateWCUri(noProtocolTypeInvalidUri).apply { assertNull(this) } - } - - @Test - fun `validate WC uri test optional data field`() { - val validUri = - "wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&relay-data=testData&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303" - - SignValidator.validateWCUri("").apply { assertEquals(null, this) } - SignValidator.validateWCUri(validUri).apply { - assertNotNull(this) - assertEquals("7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9", this!!.topic.value) - assertEquals("irn", this!!.relay.protocol) - assertEquals("testData", this.relay.data) - assertEquals("587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303", this.symKey.keyAsHex) - assertEquals("2", this.version) - } - } - - @Test - fun `parse walletconnect uri to absolute string`() { - val uri = EngineDO.WalletConnectUri( - Topic("11112222244444"), - SymmetricKey("0x12321321312312312321"), - RelayProtocolOptions("irn", "teeestData") - ) - - assertEquals(uri.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&relay-data=teeestData&symKey=0x12321321312312312321") - - val uri2 = EngineDO.WalletConnectUri( - Topic("11112222244444"), - SymmetricKey("0x12321321312312312321"), - RelayProtocolOptions("irn") - ) - - assertEquals(uri2.toAbsoluteString(), "wc:11112222244444@2?relay-protocol=irn&symKey=0x12321321312312312321") - } - - @Test - fun `extend session expiry less than 1 week`() { - val currentExpiry: Long = 1646641841 //07.03 - val newExpiry: Long = 1646901496 //10.03 - SignValidator.validateSessionExtend(newExpiry, currentExpiry) { - assertTrue(false) - } - } - - @Test - fun `extend session expiry over 1 week`() { - val currentExpiry: Long = 1646641841 //07.03 - val newExpiry: Long = 1647765496 //20.03 - - SignValidator.validateSessionExtend(newExpiry, currentExpiry) { - assertEquals(INVALID_EXTEND_TIME, it.message) - } - } - - @Test - fun `extend session expiry less than current expiry`() { - val currentExpiry: Long = 1646641841 //07.03 - val newExpiry: Long = 1646555896 //06.03 - - SignValidator.validateSessionExtend(newExpiry, currentExpiry) { - assertEquals(INVALID_EXTEND_TIME, it.message) - } - } - - @Test - fun `test time periods in seconds`() { - fiveMinutesInSeconds.apply { assertEquals(this.compareTo(300), 0) } - dayInSeconds.apply { assertEquals(this.compareTo(86400), 0) } - weekInSeconds.apply { assertEquals(this.compareTo(604800), 0) } - monthInSeconds.apply { assertEquals(this.compareTo(2592000), 0) } - } -} \ No newline at end of file diff --git a/sample/common/build.gradle.kts b/sample/common/build.gradle.kts index 6483cb47e..6ed9bf73c 100644 --- a/sample/common/build.gradle.kts +++ b/sample/common/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } android { - namespace = "com.walletconnect.sample.common" + namespace = "com.reown.sample.common" compileSdk = COMPILE_SDK defaultConfig { diff --git a/sample/common/src/main/AndroidManifest.xml b/sample/common/src/main/AndroidManifest.xml index 90fb541b3..ea3a781c2 100644 --- a/sample/common/src/main/AndroidManifest.xml +++ b/sample/common/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Beagle.kt b/sample/common/src/main/kotlin/com/reown/sample/common/Beagle.kt similarity index 83% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/Beagle.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/Beagle.kt index efb57274b..75831c990 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Beagle.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/Beagle.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common +package com.reown.sample.common import android.app.Application import com.pandulapeter.beagle.Beagle @@ -30,13 +30,4 @@ fun initBeagle(app: Application, header: HeaderModule, vararg modules: Module<*> networkLogBehavior = Behavior.NetworkLogBehavior(networkLoggers = listOf(BeagleOkHttpLogger)) ) ) - Beagle.set( - header, - ScreenCaptureToolboxModule(), - DividerModule(), - TextModule("Logs", TextModule.Type.SECTION_HEADER), - NetworkLogListModule(), - LogListModule(), - *modules - ) } diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/BottomVerticalSpaceItemDecoration.kt b/sample/common/src/main/kotlin/com/reown/sample/common/BottomVerticalSpaceItemDecoration.kt similarity index 92% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/BottomVerticalSpaceItemDecoration.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/BottomVerticalSpaceItemDecoration.kt index 2c48e3cde..a2b0c65ec 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/BottomVerticalSpaceItemDecoration.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/BottomVerticalSpaceItemDecoration.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common +package com.reown.sample.common import android.graphics.Rect import android.view.View diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Chains.kt b/sample/common/src/main/kotlin/com/reown/sample/common/Chains.kt similarity index 99% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/Chains.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/Chains.kt index dee7c5dcf..eb73955d8 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Chains.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/Chains.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common +package com.reown.sample.common import androidx.annotation.DrawableRes diff --git a/sample/common/src/main/kotlin/com/reown/sample/common/Constants.kt b/sample/common/src/main/kotlin/com/reown/sample/common/Constants.kt new file mode 100644 index 000000000..89fdb7aa5 --- /dev/null +++ b/sample/common/src/main/kotlin/com/reown/sample/common/Constants.kt @@ -0,0 +1,3 @@ +package com.reown.sample.common + +const val RELAY_URL = "relay.walletconnect.org" \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/MultipreviewAnnotations.kt b/sample/common/src/main/kotlin/com/reown/sample/common/MultipreviewAnnotations.kt similarity index 98% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/MultipreviewAnnotations.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/MultipreviewAnnotations.kt index 631f61b7b..323eca0d7 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/MultipreviewAnnotations.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/MultipreviewAnnotations.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.walletconnect.sample.common +package com.reown.sample.common import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.compose.ui.tooling.preview.Preview diff --git a/sample/common/src/main/kotlin/com/reown/sample/common/Utils.kt b/sample/common/src/main/kotlin/com/reown/sample/common/Utils.kt new file mode 100644 index 000000000..b6acea911 --- /dev/null +++ b/sample/common/src/main/kotlin/com/reown/sample/common/Utils.kt @@ -0,0 +1,82 @@ +package com.reown.sample.common + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.Observer +import androidx.viewbinding.ViewBinding +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +fun Context.sendResponseDeepLink(sessionRequestDeeplinkUri: Uri) { + try { + startActivity(Intent(Intent.ACTION_VIEW, sessionRequestDeeplinkUri)) + } catch (exception: ActivityNotFoundException) { + Log.e(tag(this), exception.stackTraceToString()) + } +} + +inline fun tag(currentClass: T): String { + return ("Wallet" + currentClass::class.java.canonicalName!!.substringAfterLast(".")).take(23) +} + +// https://github.com/Zhuinden/fragmentviewbindingdelegate-kt +// https://zhuinden.medium.com/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c +class FragmentViewBindingDelegate( + val fragment: Fragment, + val viewBindingFactory: (View) -> T, +) : ReadOnlyProperty { + private var binding: T? = null + + init { + fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { + val viewLifecycleOwnerLiveDataObserver = + Observer { + val viewLifecycleOwner = it ?: return@Observer + + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + binding = null + } + }) + } + + override fun onCreate(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) + } + + override fun onDestroy(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) + } + }) + } + + override fun getValue(thisRef: Fragment, property: KProperty<*>): T { + val binding = binding + if (binding != null) { + return binding + } + + val lifecycle = fragment.viewLifecycleOwner.lifecycle + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { + throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.") + } + + return viewBindingFactory(thisRef.requireView()).also { this.binding = it } + } +} + +fun Fragment.viewBinding(viewBindingFactory: (View) -> T): FragmentViewBindingDelegate = FragmentViewBindingDelegate(this, viewBindingFactory) + +inline fun AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T): Lazy = lazy(LazyThreadSafetyMode.NONE) { + bindingInflater(layoutInflater) +} \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/ComposeExtensions.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/ComposeExtensions.kt similarity index 98% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/ComposeExtensions.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/ui/ComposeExtensions.kt index 47336a648..2c6634548 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/ComposeExtensions.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/ComposeExtensions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common.ui +package com.reown.sample.common.ui import android.app.Activity import android.content.Context diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBar.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBar.kt similarity index 84% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBar.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBar.kt index df0e6d1c1..1113055c1 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBar.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBar.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common.ui +package com.reown.sample.common.ui import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -22,9 +22,9 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.R -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.UiModePreview +import com.reown.sample.common.R +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.UiModePreview @Composable @@ -45,7 +45,7 @@ fun WCTopAppBar( Image( modifier = Modifier .size(24.dp) - .clickable(indication = rememberRipple(bounded = false, radius = 24.dp), interactionSource = remember { MutableInteractionSource() }, onClick = actionImage.onClick), + .clickable(onClick = actionImage.onClick), painter = painterResource(id = actionImage.resource), contentDescription = null, ) diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBarLegacy.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBarLegacy.kt similarity index 91% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBarLegacy.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBarLegacy.kt index 601653974..f2076b3bd 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/WCTopAppBarLegacy.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/WCTopAppBarLegacy.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common.ui +package com.reown.sample.common.ui import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -28,8 +28,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension -import com.walletconnect.sample.common.BuildConfig -import com.walletconnect.sample.common.R +import com.reown.sample.common.BuildConfig +import com.reown.sample.common.R @Composable fun WCTopAppBarLegacy( @@ -48,22 +48,12 @@ fun WCTopAppBarLegacy( ), @DrawableRes icon: Int? = null, onIconClick: (() -> Unit)? = null, - onBackIconClick: (() -> Unit)? = null, ) { Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically ) { Spacer(modifier = Modifier.width(8.dp)) - onBackIconClick?.let { - Icon( - tint = Color(0xFF3496ff), - imageVector = ImageVector.vectorResource(id = R.drawable.chevron_left), - contentDescription = "BackArrow", - modifier = Modifier.clickable { onBackIconClick() } - ) - Spacer(modifier = Modifier.width(32.dp)) - } Row( modifier = Modifier .fillMaxWidth() diff --git a/sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Buttons.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Buttons.kt new file mode 100644 index 000000000..20e3e90f4 --- /dev/null +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Buttons.kt @@ -0,0 +1,83 @@ +package com.reown.sample.common.ui.commons + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun BlueButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val contentColor = Color(0xFFE5E7E7) + Button( + shape = RoundedCornerShape(12.dp), + modifier = modifier, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color(0xFF3496ff), + contentColor = contentColor + ), + onClick = { + onClick() + }, + ) { + Text(text = text, color = contentColor) + } +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +fun ButtonWithLoader( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + isLoading: Boolean, +) { + val contentColor = Color(0xFFE5E7E7) + AnimatedContent(targetState = isLoading, label = "Loading") { state -> + if (state) { + CircularProgressIndicator( + modifier = modifier + .size(48.dp) + .padding(8.dp) + .wrapContentWidth(align = Alignment.CenterHorizontally) + .wrapContentHeight(align = Alignment.CenterVertically), + color = contentColor, strokeWidth = 4.dp + ) + } else { + Button( + shape = RoundedCornerShape(12.dp), + modifier = modifier, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color(0xFF3496ff), + contentColor = contentColor + ), + onClick = { + onClick() + }, + ) { + Text(text = text, color = contentColor, style = TextStyle( + fontSize = 13.sp, + fontWeight = FontWeight(400), + ),) + } + } + } +} \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Loaders.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Loaders.kt similarity index 95% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Loaders.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Loaders.kt index 1f2ecbfa5..7f33c5a44 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Loaders.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/commons/Loaders.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common.ui.commons +package com.reown.sample.common.ui.commons import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -23,7 +23,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable fun FullScreenLoader( diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Color.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Color.kt similarity index 96% rename from sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Color.kt rename to sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Color.kt index 727b7a683..aa9b21bde 100644 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Color.kt +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Color.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.common.ui.theme +package com.reown.sample.common.ui.theme import androidx.compose.ui.graphics.Color diff --git a/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Theme.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Theme.kt new file mode 100644 index 000000000..dfe53a89a --- /dev/null +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Theme.kt @@ -0,0 +1,112 @@ +package com.reown.sample.common.ui.theme + +import android.app.Activity +import android.content.res.Configuration +import android.os.Build +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Colors +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.tooling.preview.Preview +import androidx.core.view.WindowCompat + +private val LightColors = lightColors( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + error = md_theme_light_error, + onError = md_theme_light_onError, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, +) + +private val DarkColors = darkColors( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + error = md_theme_dark_error, + onError = md_theme_dark_onError, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, +) + +@Composable +fun WCSampleAppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { + val colors = if (darkTheme) DarkColors else LightColors + + val view = LocalView.current + + if (!view.isInEditMode) { + SideEffect { + val currentWindow = (view.context as? Activity)?.window ?: throw Exception("Not in an activity - unable to get Window reference") + currentWindow.statusBarColor = colors.background.toArgb() + WindowCompat.getInsetsController(currentWindow, view).isAppearanceLightStatusBars = darkTheme + } + } + + SideEffect { + (view.context as Activity).window.run { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + isNavigationBarContrastEnforced = false + } + + WindowCompat.getInsetsController(this, view).run { + isAppearanceLightStatusBars = !darkTheme + isAppearanceLightNavigationBars = !darkTheme + } + } + } + + WCTheme(colors = colors, content = content) +} + +@Composable +fun PreviewTheme(content: @Composable () -> Unit) { + WCTheme( + colors = if (isSystemInDarkTheme()) DarkColors else LightColors, + content = { + Column(modifier = Modifier.background(MaterialTheme.colors.background)) { + content() + } + } + ) +} + +@Composable +internal fun WCTheme( + colors: Colors, + content: @Composable () -> Unit, +) { + MaterialTheme( + colors = colors, + typography = Typography, + content = content + ) +} + +@LightTheme +@DarkTheme +annotation class UiModePreview + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +internal annotation class LightTheme + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +internal annotation class DarkTheme diff --git a/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Type.kt b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Type.kt new file mode 100644 index 000000000..2994d1510 --- /dev/null +++ b/sample/common/src/main/kotlin/com/reown/sample/common/ui/theme/Type.kt @@ -0,0 +1,5 @@ +package com.reown.sample.common.ui.theme + +import androidx.compose.material.Typography + +val Typography = Typography() diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Constants.kt b/sample/common/src/main/kotlin/com/walletconnect/sample/common/Constants.kt deleted file mode 100644 index d4097db0a..000000000 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Constants.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.walletconnect.sample.common - -const val RELAY_URL = "relay.walletconnect.org" \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Utils.kt b/sample/common/src/main/kotlin/com/walletconnect/sample/common/Utils.kt deleted file mode 100644 index 4eb7755d7..000000000 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/Utils.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.walletconnect.sample.common - -import android.content.ActivityNotFoundException -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.Observer -import androidx.viewbinding.ViewBinding -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -fun Context.sendResponseDeepLink(sessionRequestDeeplinkUri: Uri) { - try { - startActivity(Intent(Intent.ACTION_VIEW, sessionRequestDeeplinkUri)) - } catch (exception: ActivityNotFoundException) { - Log.e(tag(this), exception.stackTraceToString()) - } -} - -inline fun tag(currentClass: T): String { - return ("Wallet" + currentClass::class.java.canonicalName!!.substringAfterLast(".")).take(23) -} - -// https://github.com/Zhuinden/fragmentviewbindingdelegate-kt -// https://zhuinden.medium.com/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c -class FragmentViewBindingDelegate( - val fragment: Fragment, - val viewBindingFactory: (View) -> T, -) : ReadOnlyProperty { - private var binding: T? = null - - init { - fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { - val viewLifecycleOwnerLiveDataObserver = - Observer { - val viewLifecycleOwner = it ?: return@Observer - - viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - binding = null - } - }) - } - - override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) - } - - override fun onDestroy(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) - } - }) - } - - override fun getValue(thisRef: Fragment, property: KProperty<*>): T { - val binding = binding - if (binding != null) { - return binding - } - - val lifecycle = fragment.viewLifecycleOwner.lifecycle - if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { - throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.") - } - - return viewBindingFactory(thisRef.requireView()).also { this.binding = it } - } -} - -fun Fragment.viewBinding(viewBindingFactory: (View) -> T): FragmentViewBindingDelegate = FragmentViewBindingDelegate(this, viewBindingFactory) - -inline fun AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T): Lazy = lazy(LazyThreadSafetyMode.NONE) { - bindingInflater(layoutInflater) -} \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Buttons.kt b/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Buttons.kt deleted file mode 100644 index 7ab87442e..000000000 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/commons/Buttons.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.walletconnect.sample.common.ui.commons - -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -@Composable -fun BlueButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - val contentColor = Color(0xFFE5E7E7) - Button( - shape = RoundedCornerShape(12.dp), - modifier = modifier, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color(0xFF3496ff), - contentColor = contentColor - ), - onClick = { - onClick() - }, - ) { - Text(text = text, color = contentColor) - } -} - -@OptIn(ExperimentalAnimationApi::class) -@Composable -fun ButtonWithLoader( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, - isLoading: Boolean, -) { - val contentColor = Color(0xFFE5E7E7) - AnimatedContent(targetState = isLoading, label = "Loading") { state -> - if (state) { - CircularProgressIndicator( - modifier = modifier - .size(48.dp) - .padding(8.dp) - .wrapContentWidth(align = Alignment.CenterHorizontally) - .wrapContentHeight(align = Alignment.CenterVertically), - color = contentColor, strokeWidth = 4.dp - ) - } else { - Button( - shape = RoundedCornerShape(12.dp), - modifier = modifier, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color(0xFF3496ff), - contentColor = contentColor - ), - onClick = { - onClick() - }, - ) { - Text(text = text, color = contentColor, style = TextStyle( - fontSize = 13.sp, - fontWeight = FontWeight(400), - ),) - } - } - } -} \ No newline at end of file diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Theme.kt b/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Theme.kt deleted file mode 100644 index 8eaaa9414..000000000 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Theme.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.walletconnect.sample.common.ui.theme - -import android.app.Activity -import android.content.res.Configuration -import android.os.Build -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Column -import androidx.compose.material.Colors -import androidx.compose.material.MaterialTheme -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.tooling.preview.Preview -import androidx.core.view.WindowCompat - -private val LightColors = lightColors( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - error = md_theme_light_error, - onError = md_theme_light_onError, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, -) - -private val DarkColors = darkColors( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - error = md_theme_dark_error, - onError = md_theme_dark_onError, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, -) - -@Composable -fun WCSampleAppTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit, -) { - val colors = if (darkTheme) DarkColors else LightColors - - val view = LocalView.current - - if (!view.isInEditMode) { - SideEffect { - val currentWindow = (view.context as? Activity)?.window ?: throw Exception("Not in an activity - unable to get Window reference") - currentWindow.statusBarColor = colors.background.toArgb() - WindowCompat.getInsetsController(currentWindow, view).isAppearanceLightStatusBars = darkTheme - } - } - - SideEffect { - (view.context as Activity).window.run { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isNavigationBarContrastEnforced = false - } - - WindowCompat.getInsetsController(this, view).run { - isAppearanceLightStatusBars = !darkTheme - isAppearanceLightNavigationBars = !darkTheme - } - } - } - - WCTheme(colors = colors, content = content) -} - -@Composable -fun PreviewTheme(content: @Composable () -> Unit) { - WCTheme( - colors = if (isSystemInDarkTheme()) DarkColors else LightColors, - content = { - Column(modifier = Modifier.background(MaterialTheme.colors.background)) { - content() - } - } - ) -} - -@Composable -internal fun WCTheme( - colors: Colors, - content: @Composable () -> Unit, -) { - MaterialTheme( - colors = colors, - typography = Typography, - content = content - ) -} - -@LightTheme -@DarkTheme -annotation class UiModePreview - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -internal annotation class LightTheme - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -internal annotation class DarkTheme diff --git a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Type.kt b/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Type.kt deleted file mode 100644 index 93dccff24..000000000 --- a/sample/common/src/main/kotlin/com/walletconnect/sample/common/ui/theme/Type.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.sample.common.ui.theme - -import androidx.compose.material.Typography - -val Typography = Typography() diff --git a/sample/dapp/build.gradle.kts b/sample/dapp/build.gradle.kts index 202cee083..1cf769692 100644 --- a/sample/dapp/build.gradle.kts +++ b/sample/dapp/build.gradle.kts @@ -9,11 +9,11 @@ plugins { } android { - namespace = "com.walletconnect.sample.dapp" + namespace = "com.reown.sample.dapp" compileSdk = COMPILE_SDK defaultConfig { - applicationId = "com.walletconnect.sample.dapp" + applicationId = "com.reown.sample.dapp" minSdk = MIN_SDK targetSdk = TARGET_SDK versionName = SAMPLE_VERSION_NAME @@ -23,24 +23,24 @@ android { useSupportLibrary = true } buildConfigField("String", "PROJECT_ID", "\"${System.getenv("WC_CLOUD_PROJECT_ID") ?: ""}\"") - buildConfigField("String", "BOM_VERSION", "\"${BOM_VERSION ?: ""}\"") + buildConfigField("String", "BOM_VERSION", "\"${BOM_VERSION}\"") } buildTypes { getByName("release") { manifestPlaceholders["pathPrefix"] = "/dapp_release" - buildConfigField("String", "DAPP_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/dapp_release\"") + buildConfigField("String", "DAPP_APP_LINK", "\"https://dev.lab.web3modal.com/dapp_release\"") } getByName("internal") { manifestPlaceholders["pathPrefix"] = "/dapp_internal" - buildConfigField("String", "DAPP_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/dapp_internal\"") + buildConfigField("String", "DAPP_APP_LINK", "\"https://dev.lab.web3modal.com/dapp_internal\"") } getByName("debug") { manifestPlaceholders["pathPrefix"] = "/dapp_debug" - buildConfigField("String", "DAPP_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/dapp_debug\"") + buildConfigField("String", "DAPP_APP_LINK", "\"https://dev.lab.web3modal.com/dapp_debug\"") } } @@ -78,8 +78,9 @@ dependencies { implementation("io.insert-koin:koin-androidx-compose:3.4.3") implementation("io.coil-kt:coil-compose:2.3.0") - implementation("androidmads.library.qrgenearator:QRGenearator:1.0.4") + implementation("androidx.compose.material:material-icons-core:1.7.1") + implementation(libs.qrCodeGenerator) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.tooling.preview) @@ -97,15 +98,12 @@ dependencies { implementation(libs.bundles.firebase) debugImplementation(project(":core:android")) - debugImplementation(project(":product:walletconnectmodal")) - debugImplementation(project(":protocol:sign")) + debugImplementation(project(":product:appkit")) internalImplementation(project(":core:android")) - internalImplementation(project(":product:walletconnectmodal")) - internalImplementation(project(":protocol:sign")) + internalImplementation(project(":product:appkit")) - releaseImplementation(platform("com.walletconnect:android-bom:$BOM_VERSION")) - releaseImplementation("com.walletconnect:android-core") - releaseImplementation("com.walletconnect:walletconnect-modal") - releaseImplementation("com.walletconnect:sign") + releaseImplementation(platform("com.reown:android-bom:$BOM_VERSION")) + releaseImplementation("com.reown:android-core") + releaseImplementation("com.reown:appkit") } diff --git a/sample/dapp/src/main/AndroidManifest.xml b/sample/dapp/src/main/AndroidManifest.xml index 55a113003..1aac7378d 100644 --- a/sample/dapp/src/main/AndroidManifest.xml +++ b/sample/dapp/src/main/AndroidManifest.xml @@ -14,9 +14,9 @@ - - - + + + @@ -31,7 +31,7 @@ android:name=".DappSampleApp" android:theme="@style/Theme.WalletConnect"> diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/DappSampleApp.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/DappSampleApp.kt new file mode 100644 index 000000000..1315a7bef --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/DappSampleApp.kt @@ -0,0 +1,60 @@ +package com.reown.sample.dapp + +import android.app.Application +import com.google.firebase.appdistribution.FirebaseAppDistribution +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.sample.common.tag +import com.reown.util.bytesToHex +import com.reown.util.randomBytes +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.Modal +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.utils.EthUtils +import timber.log.Timber +import com.reown.sample.common.BuildConfig as CommonBuildConfig + +class DappSampleApp : Application() { + + override fun onCreate() { + super.onCreate() + + val appMetaData = Core.Model.AppMetaData( + name = "Kotlin Dapp", + description = "Kotlin Dapp Implementation", + url = "https://dev.lab.web3modal.com", + icons = listOf("https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"), + redirect = "kotlin-dapp-wc://request", + appLink = BuildConfig.DAPP_APP_LINK, + linkMode = true + ) + + CoreClient.initialize( + application = this, + projectId = CommonBuildConfig.PROJECT_ID, + metaData = appMetaData, + ) { + Firebase.crashlytics.recordException(it.throwable) + } + + AppKit.initialize(Modal.Params.Init(core = CoreClient)) { error -> + Timber.e(tag(this), error.throwable.stackTraceToString()) + } + + AppKit.setChains(AppKitChainsPresets.ethChains.values.toList()) +// +// val authParams = Modal.Model.AuthPayloadParams( +// chains = AppKitChainsPresets.ethChains.values.toList().map { it.id }, +// domain = "sample.kotlin.modal", +// uri = "https://web3inbox.com/all-apps", +// nonce = randomBytes(12).bytesToHex(), +// statement = "I accept the Terms of Service: https://yourDappDomain.com/", +// methods = EthUtils.ethMethods +// ) +// AppKit.setAuthRequestParams(authParams) + + FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() + } +} \ No newline at end of file diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/domain/DappDelegate.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/domain/DappDelegate.kt new file mode 100644 index 000000000..adf8920a9 --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/domain/DappDelegate.kt @@ -0,0 +1,136 @@ +package com.reown.sample.dapp.domain + +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.sample.common.tag +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.Modal +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import timber.log.Timber + +object DappDelegate : AppKit.ModalDelegate, CoreClient.CoreDelegate { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() + val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() + private val _coreEvents: MutableSharedFlow = MutableSharedFlow() + val coreEvents: SharedFlow = _coreEvents.asSharedFlow() + private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) + val connectionState: SharedFlow = _connectionState.asSharedFlow() + + var selectedSessionTopic: String? = null + private set + + init { + AppKit.setDelegate(this) + CoreClient.setDelegate(this) + } + + override fun onConnectionStateChange(state: Modal.Model.ConnectionState) { + Timber.d(tag(this), "onConnectionStateChange($state)") + scope.launch { + _connectionState.emit(state) + } + } + + override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { + selectedSessionTopic = (approvedSession as Modal.Model.ApprovedSession.WalletConnectSession).topic + + scope.launch { + _wcEventModels.emit(approvedSession) + } + } + + override fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) { + scope.launch { + _wcEventModels.emit(rejectedSession) + } + } + + override fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) { + scope.launch { + _wcEventModels.emit(updatedSession) + } + } + + override fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) { + scope.launch { + _wcEventModels.emit(sessionEvent) + } + } + + override fun onSessionEvent(sessionEvent: Modal.Model.Event) { + scope.launch { + _wcEventModels.emit(sessionEvent) + } + } + + override fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) { + deselectAccountDetails() + + scope.launch { + _wcEventModels.emit(deletedSession) + } + } + + override fun onSessionExtend(session: Modal.Model.Session) { + scope.launch { + _wcEventModels.emit(session) + } + } + + override fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) { + scope.launch { + _wcEventModels.emit(response) + } + } + + override fun onSessionAuthenticateResponse(sessionUpdateResponse: Modal.Model.SessionAuthenticateResponse) { + if (sessionUpdateResponse is Modal.Model.SessionAuthenticateResponse.Result) { + selectedSessionTopic = sessionUpdateResponse.session?.topic + } + scope.launch { + _wcEventModels.emit(sessionUpdateResponse) + } + } + + override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) { + scope.launch { + _wcEventModels.emit(proposal) + } + } + + override fun onRequestExpired(request: Modal.Model.ExpiredRequest) { + scope.launch { + _wcEventModels.emit(request) + } + } + + fun deselectAccountDetails() { + selectedSessionTopic = null + } + + override fun onError(error: Modal.Model.Error) { + Timber.d(tag(this), error.throwable.stackTraceToString()) + scope.launch { + _wcEventModels.emit(error) + } + } + + override fun onPairingDelete(deletedPairing: Core.Model.DeletedPairing) { + //Deprecated - pairings are automatically deleted + } + + override fun onPairingExpired(expiredPairing: Core.Model.ExpiredPairing) { + //Deprecated - pairings are automatically expired + } + + override fun onPairingState(pairingState: Core.Model.PairingState) { + Timber.d(tag(this), "Dapp pairing state: ${pairingState.isPairingState}") + } +} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleActivity.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleActivity.kt similarity index 79% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleActivity.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleActivity.kt index c8f7c0234..8e427445a 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleActivity.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleActivity.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialApi::class) -package com.walletconnect.sample.dapp.ui +package com.reown.sample.dapp.ui import android.content.Intent import android.os.Bundle @@ -10,9 +10,9 @@ import androidx.activity.compose.setContent import androidx.compose.material.ExperimentalMaterialApi import androidx.lifecycle.lifecycleScope import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.walletconnect.sample.common.ui.theme.WCSampleAppTheme -import com.walletconnect.sample.dapp.ui.routes.host.DappSampleHost -import com.walletconnect.sign.client.SignClient +import com.reown.appkit.client.AppKit +import com.reown.sample.common.ui.theme.WCSampleAppTheme +import com.reown.sample.dapp.ui.routes.host.DappSampleHost import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -20,14 +20,14 @@ class DappSampleActivity : ComponentActivity() { @ExperimentalMaterialNavigationApi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent() { + setContent { WCSampleAppTheme { DappSampleHost() } } if (intent?.dataString?.contains("wc_ev") == true) { - SignClient.dispatchEnvelope(intent.dataString ?: "") { + AppKit.handleDeepLink(intent.dataString ?: "") { lifecycleScope.launch(Dispatchers.Main) { Toast.makeText(this@DappSampleActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() } @@ -39,7 +39,7 @@ class DappSampleActivity : ComponentActivity() { super.onNewIntent(intent) if (intent?.dataString?.contains("wc_ev") == true) { - SignClient.dispatchEnvelope(intent.dataString ?: "") { + AppKit.handleDeepLink(intent.dataString ?: "") { lifecycleScope.launch(Dispatchers.Main) { Toast.makeText(this@DappSampleActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() } diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleEvents.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleEvents.kt similarity index 96% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleEvents.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleEvents.kt index 5dca52941..00a7fd8ed 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleEvents.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleEvents.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.dapp.ui +package com.reown.sample.dapp.ui sealed class DappSampleEvents { diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleNavGraph.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleNavGraph.kt new file mode 100644 index 000000000..af18f8a69 --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/DappSampleNavGraph.kt @@ -0,0 +1,63 @@ +@file:OptIn(ExperimentalMaterialNavigationApi::class) + +package com.reown.sample.dapp.ui + +import android.annotation.SuppressLint +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.navArgument +import com.google.accompanist.navigation.material.BottomSheetNavigator +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.google.accompanist.navigation.material.ModalBottomSheetLayout +import com.reown.sample.dapp.ui.routes.Route +import com.reown.sample.dapp.ui.routes.composable_routes.account.AccountRoute +import com.reown.sample.dapp.ui.routes.composable_routes.chain_selection.ChainSelectionRoute +import com.reown.sample.dapp.ui.routes.composable_routes.session.SessionRoute +import com.reown.appkit.ui.appKitGraph + +@SuppressLint("RestrictedApi") +@Composable +fun DappSampleNavGraph( + bottomSheetNavigator: BottomSheetNavigator, + navController: NavHostController, + startDestination: String, +) { + ModalBottomSheetLayout( + bottomSheetNavigator = bottomSheetNavigator, + sheetBackgroundColor = Color.Transparent, + sheetElevation = 0.dp, + scrimColor = Color.Unspecified, + sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) + ) { + NavHost( + navController = navController, + startDestination = startDestination + ) { + composable(Route.ChainSelection.path) { + ChainSelectionRoute(navController) + } + composable(Route.Session.path) { + SessionRoute(navController) + } + composable( + route = Route.Account.path + "/{$accountArg}", + arguments = listOf(navArgument(accountArg) { type = NavType.StringType }) + ) { + AccountRoute(navController) + } + appKitGraph(navController) + } + } +} + +const val accountArg = "accountArg" +fun NavController.navigateToAccount(selectedAccount: String) { + navigate(Route.Account.path + "/$selectedAccount") +} diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/Route.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/Route.kt new file mode 100644 index 000000000..dd89b0adf --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/Route.kt @@ -0,0 +1,7 @@ +package com.reown.sample.dapp.ui.routes + +sealed class Route(val path: String) { + object ChainSelection : Route("chain_selection") + object Session : Route("session") + object Account : Route("account") +} diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt new file mode 100644 index 000000000..9938c9d20 --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt @@ -0,0 +1,172 @@ +package com.reown.sample.dapp.ui.routes.composable_routes.account + +import android.content.Intent +import android.net.Uri +import android.widget.Toast +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import coil.compose.AsyncImage +import com.reown.sample.common.ui.commons.BlueButton +import com.reown.sample.common.ui.commons.FullScreenLoader +import com.reown.sample.common.ui.commons.Loader +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.sample.dapp.ui.routes.Route +import timber.log.Timber + +@Composable +fun AccountRoute(navController: NavController) { + val viewModel: AccountViewModel = viewModel() + val state by viewModel.uiState.collectAsState() + val awaitResponse by viewModel.awaitResponse.collectAsState(false) + val context = LocalContext.current + + LaunchedEffect(Unit) { + viewModel.fetchAccountDetails() + + viewModel.events.collect { event -> + when (event) { + is DappSampleEvents.RequestSuccess -> { + Toast.makeText(context, "Result: ${event.result}", Toast.LENGTH_LONG).show() + } + + is DappSampleEvents.RequestPeerError -> { + Toast.makeText(context, "Error: ${event.errorMsg}", Toast.LENGTH_LONG).show() + } + + is DappSampleEvents.RequestError -> { + Toast.makeText(context, "Error: ${event.exceptionMsg}", Toast.LENGTH_LONG).show() + } + + is DappSampleEvents.Disconnect -> navController.navigate(Route.ChainSelection.path) { + popUpTo(navController.graph.startDestinationId) { + inclusive = true + } + } + + else -> Unit + } + } + } + + AccountScreen( + state = state, + onMethodClick = viewModel::requestMethod, + awaitResponse = awaitResponse + ) +} + +@Composable +private fun AccountScreen( + state: AccountUi, + onMethodClick: (String, (Uri) -> Unit) -> Unit, + awaitResponse: Boolean, +) { + when (state) { + AccountUi.Loading -> FullScreenLoader() + is AccountUi.AccountData -> AccountContent(state, onMethodClick, awaitResponse) + } +} + +@Composable +fun AccountContent( + state: AccountUi.AccountData, + onMethodClick: (String, (Uri) -> Unit) -> Unit, + awaitResponse: Boolean, +) { + Box { + Column( + modifier = Modifier + .fillMaxSize() + .padding(20.dp) + ) { + ChainData(chain = state) + Spacer(modifier = Modifier.height(6.dp)) + MethodList( + methods = state.listOfMethods, + onMethodClick = onMethodClick + ) + } + + if (awaitResponse) { + Loader() + } + } +} + +@Composable +private fun ChainData(chain: AccountUi.AccountData) { + Column( + modifier = Modifier + .clickable { } + .fillMaxWidth() + .padding(horizontal = 24.dp, 16.dp) + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + AsyncImage(model = chain.icon, contentDescription = null, Modifier.size(48.dp)) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = chain.chainName, + style = TextStyle(fontWeight = FontWeight.SemiBold, fontSize = 18.sp) + ) + } + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = chain.account, + style = TextStyle(fontSize = 12.sp), + maxLines = 1, + modifier = Modifier.padding(horizontal = 6.dp), + overflow = TextOverflow.Ellipsis, + ) + } +} + +@Composable +private fun MethodList( + methods: List, + onMethodClick: (String, (Uri) -> Unit) -> Unit, +) { + val context = LocalContext.current + LazyColumn(modifier = Modifier.fillMaxWidth()) { + itemsIndexed(methods) { _, item -> + BlueButton( + text = item, + onClick = { + onMethodClick(item) { uri -> + try { + context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + } catch (e: Exception) { + Timber.tag("AccountRoute").d("Activity not found: $e") + } + } + }, + modifier = Modifier.fillMaxWidth(), + ) + } + } +} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt similarity index 78% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt index bb77e86b3..64f37eec1 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountUi.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.account +package com.reown.sample.dapp.ui.routes.composable_routes.account sealed class AccountUi { diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt new file mode 100644 index 000000000..41609f491 --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt @@ -0,0 +1,149 @@ +package com.reown.sample.dapp.ui.routes.composable_routes.account + +import android.net.Uri +import androidx.core.net.toUri +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.reown.sample.common.Chains +import com.reown.sample.common.getEthSendTransaction +import com.reown.sample.common.getEthSignBody +import com.reown.sample.common.getEthSignTypedData +import com.reown.sample.common.getPersonalSignBody +import com.reown.sample.dapp.domain.DappDelegate +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.sample.dapp.ui.accountArg +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.Modal +import com.reown.appkit.client.models.Session +import com.reown.appkit.client.models.request.Request +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +class AccountViewModel( + savedStateHandle: SavedStateHandle +) : ViewModel() { + private val selectedAccountInfo = checkNotNull(savedStateHandle.get(accountArg)) + + private val _uiState: MutableStateFlow = MutableStateFlow(AccountUi.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _awaitResponse: MutableStateFlow = MutableStateFlow(false) + val awaitResponse: StateFlow = _awaitResponse.asStateFlow() + + private val _events: MutableSharedFlow = MutableSharedFlow() + val events: SharedFlow + get() = _events.asSharedFlow() + + init { + DappDelegate.wcEventModels + .filterNotNull() + .onEach { walletEvent -> + when (walletEvent) { + is Modal.Model.UpdatedSession -> fetchAccountDetails() + is Modal.Model.SessionRequestResponse -> { + val request = when (walletEvent.result) { + is Modal.Model.JsonRpcResponse.JsonRpcResult -> { + _awaitResponse.value = false + val successResult = (walletEvent.result as Modal.Model.JsonRpcResponse.JsonRpcResult) + DappSampleEvents.RequestSuccess(successResult.result) + } + + is Modal.Model.JsonRpcResponse.JsonRpcError -> { + _awaitResponse.value = false + val errorResult = (walletEvent.result as Modal.Model.JsonRpcResponse.JsonRpcError) + DappSampleEvents.RequestPeerError("Error Message: ${errorResult.message}\n Error Code: ${errorResult.code}") + } + } + + _events.emit(request) + } + + is Modal.Model.ExpiredRequest -> { + _awaitResponse.value = false + _events.emit(DappSampleEvents.RequestError("Request expired")) + } + + is Modal.Model.DeletedSession -> { + _events.emit(DappSampleEvents.Disconnect) + } + + else -> Unit + } + } + .launchIn(viewModelScope) + } + + fun requestMethod(method: String, sendSessionRequestDeepLink: (Uri) -> Unit) { + (uiState.value as? AccountUi.AccountData)?.let { currentState -> + try { + _awaitResponse.value = true + + val (parentChain, chainId, account) = currentState.selectedAccount.split(":") + val params: String = when { + method.equals("personal_sign", true) -> getPersonalSignBody(account) + method.equals("eth_sign", true) -> getEthSignBody(account) + method.equals("eth_sendTransaction", true) -> getEthSendTransaction(account) + method.equals("eth_signTypedData", true) -> getEthSignTypedData(account) + else -> "[]" + } + val requestParams = Request( + method = method, + params = params, // stringified JSON + ) + + AppKit.request(requestParams, + onSuccess = { _ -> + (AppKit.getSession() as Session.WalletConnectSession).redirect?.toUri()?.let { deepLinkUri -> sendSessionRequestDeepLink(deepLinkUri) } + }, + onError = { + viewModelScope.launch { + _awaitResponse.value = false + _events.emit(DappSampleEvents.RequestError(it.localizedMessage ?: "Error trying to send request")) + } + }) + } catch (e: Exception) { + viewModelScope.launch { + _awaitResponse.value = false + _events.emit(DappSampleEvents.RequestError(e.localizedMessage ?: "Error trying to send request")) + } + } + } + } + + fun fetchAccountDetails() { + val (chainNamespace, chainReference, account) = selectedAccountInfo.split(":") + val chainDetails = Chains.values().first { + it.chainNamespace == chainNamespace && it.chainReference == chainReference + } + + val listOfMethodsByChainId: List = + (AppKit.getSession() as Session.WalletConnectSession).namespaces + .filter { (namespaceKey, _) -> namespaceKey == chainDetails.chainId } + .flatMap { (_, namespace) -> namespace.methods } + + + val listOfMethodsByNamespace: List = + (AppKit.getSession() as Session.WalletConnectSession).namespaces + .filter { (namespaceKey, _) -> namespaceKey == chainDetails.chainNamespace } + .flatMap { (_, namespace) -> namespace.methods } + + viewModelScope.launch { + _uiState.value = AccountUi.AccountData( + icon = chainDetails.icon, + chainName = chainDetails.chainName, + account = account, + listOfMethods = listOfMethodsByChainId.ifEmpty { listOfMethodsByNamespace }, + selectedAccount = selectedAccountInfo + ) + } + } +} \ No newline at end of file diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt similarity index 89% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt index 514701c56..19d5f16a4 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/chain_selection/ChainSelectionRoute.kt @@ -1,16 +1,11 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.chain_selection +package com.reown.sample.dapp.ui.routes.composable_routes.chain_selection import android.content.Context -import android.widget.Toast import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri -import androidmads.library.qrgenearator.QRGContents -import androidmads.library.qrgenearator.QRGEncoder -import java.net.URLEncoder +import android.graphics.drawable.Drawable +import android.widget.Toast import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.core.net.toUri import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -44,7 +39,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.LocalClipboardManager @@ -59,31 +53,32 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog +import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import com.walletconnect.android.utils.isPackageInstalled -import com.walletconnect.sample.common.Chains -import com.walletconnect.sample.common.CompletePreviews -import com.walletconnect.sample.common.ui.WCTopAppBarLegacy -import com.walletconnect.sample.common.ui.coloredShadow -import com.walletconnect.sample.common.ui.commons.BlueButton -import com.walletconnect.sample.common.ui.conditionalModifier -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.common.ui.toColor -import com.walletconnect.sample.dapp.BuildConfig -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.sample.dapp.ui.routes.Route -import com.walletconnect.sign.client.Sign -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import com.walletconnect.wcmodal.ui.openWalletConnectModal -import com.walletconnect.wcmodal.ui.state.rememberModalState +import com.github.alexzhirkevich.customqrgenerator.QrData +import com.github.alexzhirkevich.customqrgenerator.vector.QrCodeDrawable +import com.google.accompanist.drawablepainter.rememberDrawablePainter +import com.reown.android.utils.isPackageInstalled +import com.reown.appkit.ui.components.button.rememberAppKitState +import com.reown.appkit.ui.openAppKit +import com.reown.sample.common.Chains +import com.reown.sample.common.CompletePreviews +import com.reown.sample.common.ui.WCTopAppBarLegacy +import com.reown.sample.common.ui.coloredShadow +import com.reown.sample.common.ui.commons.BlueButton +import com.reown.sample.common.ui.conditionalModifier +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.themedColor +import com.reown.sample.common.ui.toColor +import com.reown.sample.dapp.BuildConfig +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.sample.dapp.ui.routes.Route import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import java.net.URLEncoder @Composable fun ChainSelectionRoute(navController: NavController, dispatcher: CoroutineDispatcher = Dispatchers.Main) { @@ -91,7 +86,7 @@ fun ChainSelectionRoute(navController: NavController, dispatcher: CoroutineDispa val composableScope = rememberCoroutineScope() val viewModel: ChainSelectionViewModel = viewModel() val chainsState by viewModel.uiState.collectAsState() - rememberModalState(navController = navController) + rememberAppKitState(navController = navController) val awaitingProposalResponse = viewModel.awaitingSharedFlow.collectAsState(false).value var pairingUri by remember { mutableStateOf(PairingUri(uri = "", isReCaps = false)) } @@ -264,7 +259,7 @@ private fun ChainSelectionScreen( onChainClick, ) BlueButton( - text = "Connect via WalletConnect Modal", + text = "Connect via AppKit", onClick = onConnectClick, modifier = Modifier .padding(vertical = 10.dp) @@ -276,9 +271,9 @@ private fun ChainSelectionScreen( text = "1-CA Link Mode (Kotlin Sample Wallet)", onClick = { val applink = when { - context.packageManager.isPackageInstalled(SAMPLE_WALLET_DEBUG_PACKAGE) -> "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_debug" - context.packageManager.isPackageInstalled(SAMPLE_WALLET_INTERNAL_PACKAGE) -> "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_internal" - context.packageManager.isPackageInstalled(SAMPLE_WALLET_RELEASE_PACKAGE) -> "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_release" + context.packageManager.isPackageInstalled(SAMPLE_WALLET_DEBUG_PACKAGE) -> "https://dev.lab.web3modal.com/wallet_debug" + context.packageManager.isPackageInstalled(SAMPLE_WALLET_INTERNAL_PACKAGE) -> "https://dev.lab.web3modal.com/wallet_internal" + context.packageManager.isPackageInstalled(SAMPLE_WALLET_RELEASE_PACKAGE) -> "https://dev.lab.web3modal.com/wallet_release" else -> "" } onAuthenticateLinkMode(applink) @@ -330,7 +325,7 @@ private fun ChainSelectionScreen( @Composable private fun QRDialog(composableScope: CoroutineScope, dispatcher: CoroutineDispatcher, pairingUri: PairingUri, onDismissRequest: () -> Unit, context: Context) { - val qrBitmap = generateQRCode(pairingUri.uri) + val qrDrawable = generateQRCode(pairingUri.uri) val clipboardManager: ClipboardManager = LocalClipboardManager.current Dialog(onDismissRequest = { onDismissRequest() }) { @@ -342,9 +337,9 @@ private fun QRDialog(composableScope: CoroutineScope, dispatcher: CoroutineDispa contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - qrBitmap?.let { + qrDrawable?.let { Image( - bitmap = it.asImageBitmap(), + painter = rememberDrawablePainter(drawable = it), contentDescription = "QR Code", modifier = Modifier .fillMaxWidth() @@ -447,10 +442,10 @@ private fun onDynamicSwitcher( } } -fun generateQRCode(content: String): Bitmap? { - val qrgEncoder = QRGEncoder(content, null, QRGContents.Type.TEXT, 400) +fun generateQRCode(content: String): Drawable? { + val qrgEncoder = QrCodeDrawable(QrData.Url(content)) return try { - qrgEncoder.bitmap + qrgEncoder } catch (e: Exception) { e.printStackTrace() null @@ -469,7 +464,12 @@ private fun handleSignEvents( when (event) { DappSampleEvents.SessionApproved -> { viewModel.awaitingProposalResponse(false) - navController.navigate(Route.Session.path) + navController.navigate(Route.Session.path) { + popUpTo(0) { + inclusive = true + } + launchSingleTop = true + } } DappSampleEvents.SessionRejected -> { @@ -487,7 +487,12 @@ private fun handleSignEvents( if (event.message != null) { Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show() } else { - navController.navigate(Route.Session.path) + navController.navigate(Route.Session.path) { + popUpTo(0) { + inclusive = true + } + launchSingleTop = true + } } } @@ -509,8 +514,7 @@ private fun onConnectClick( context: Context ) { if (viewModel.isAnyChainSelected) { - WalletConnectModal.setSessionParams(viewModel.getSessionParams()) - navController.openWalletConnectModal() + navController.openAppKit() } else { Toast.makeText(context, "Please select a chain", Toast.LENGTH_SHORT).show() } @@ -625,9 +629,9 @@ private class ChainSelectionStateProvider : PreviewParameterProvider Unit, onError: (String) -> Unit = {}) { + fun authenticate(authenticateParams: Modal.Params.Authenticate, appLink: String = "", onAuthenticateSuccess: (String?) -> Unit, onError: (String) -> Unit = {}) { viewModelScope.launch { _awaitingProposalSharedFlow.emit(true) } - SignClient.authenticate(authenticateParams, walletAppLink = appLink, + AppKit.authenticate(authenticateParams, walletAppLink = appLink, onSuccess = { url -> viewModelScope.launch { _awaitingProposalSharedFlow.emit(false) @@ -125,7 +117,7 @@ class ChainSelectionViewModel : ViewModel() { pairing = pairing ) - WalletConnectModal.connect(connectParams, + AppKit.connect(connectParams, onSuccess = { url -> if (pairingTopicPosition == -1) { viewModelScope.launch { @@ -196,7 +188,7 @@ class ChainSelectionViewModel : ViewModel() { val authenticateParams - get() = Sign.Params.Authenticate( + get() = Modal.Params.Authenticate( chains = uiState.value.filter { it.isSelected }.map { it.chainId }, domain = "sample.kotlin.dapp", uri = "https://web3inbox.com/all-apps", @@ -214,7 +206,7 @@ class ChainSelectionViewModel : ViewModel() { ) val siweParams - get() = Sign.Params.Authenticate( + get() = Modal.Params.Authenticate( chains = uiState.value.filter { it.isSelected }.map { it.chainId }, domain = "sample.kotlin.dapp", uri = "https://web3inbox.com/all-apps", diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt similarity index 91% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt index e4cffed0c..f39eb3c8a 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalFoundationApi::class) -package com.walletconnect.sample.dapp.ui.routes.composable_routes.session +package com.reown.sample.dapp.ui.routes.composable_routes.session import android.widget.Toast import androidx.compose.animation.animateContentSize @@ -48,13 +48,13 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import coil.compose.AsyncImage -import com.walletconnect.sample.common.ui.WCTopAppBarLegacy -import com.walletconnect.sample.common.ui.commons.ButtonWithLoader -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.dapp.R -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.sample.dapp.ui.navigateToAccount -import com.walletconnect.sample.dapp.ui.routes.Route +import com.reown.sample.common.ui.WCTopAppBarLegacy +import com.reown.sample.common.ui.commons.ButtonWithLoader +import com.reown.sample.common.ui.themedColor +import com.reown.sample.dapp.R +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.sample.dapp.ui.navigateToAccount +import com.reown.sample.dapp.ui.routes.Route @Composable fun SessionRoute( @@ -82,7 +82,11 @@ fun SessionRoute( is DappSampleEvents.PingLoading -> isPingLoading = true is DappSampleEvents.Disconnect -> { isDisconnectLoading = false - navController.popBackStack(Route.ChainSelection.path, inclusive = false) + navController.navigate(Route.ChainSelection.path) { + popUpTo(navController.graph.startDestinationId) { + inclusive = true + } + } Toast.makeText(context, "Disconnected successfully", Toast.LENGTH_SHORT).show() } @@ -99,7 +103,6 @@ fun SessionRoute( SessionScreen( uiState = state, - onBackPressed = navController::popBackStack, onSessionClick = navController::navigateToAccount, onPingClick = viewModel::ping, onDisconnectClick = viewModel::disconnect, @@ -111,7 +114,6 @@ fun SessionRoute( @Composable private fun SessionScreen( uiState: List, - onBackPressed: () -> Unit, onSessionClick: (String) -> Unit, onPingClick: () -> Unit, onDisconnectClick: () -> Unit, @@ -119,10 +121,7 @@ private fun SessionScreen( isPingLoading: Boolean, ) { Column { - WCTopAppBarLegacy( - titleText = "Session Chains", - onBackIconClick = onBackPressed, - ) + WCTopAppBarLegacy(titleText = "Session Chains",) ChainsAction(onPingClick, onDisconnectClick, isDisconnectLoading, isPingLoading) LazyColumn(modifier = Modifier.fillMaxSize()) { item { diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt new file mode 100644 index 000000000..d215d2b90 --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt @@ -0,0 +1,9 @@ +package com.reown.sample.dapp.ui.routes.composable_routes.session + +data class SessionUi( + val icon: Int, + val name: String, + val address: String, + val chainNamespace: String, + val chainReference: String, +) diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt new file mode 100644 index 000000000..41890dc4b --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt @@ -0,0 +1,120 @@ +package com.reown.sample.dapp.ui.routes.composable_routes.session + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.Modal +import com.reown.appkit.client.models.Session +import com.reown.sample.common.Chains +import com.reown.sample.common.tag +import com.reown.sample.dapp.domain.DappDelegate +import com.reown.sample.dapp.ui.DappSampleEvents +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import timber.log.Timber + +class SessionViewModel : ViewModel() { + + private val _sessionUI: MutableStateFlow> = MutableStateFlow(getSessions()) + val uiState: StateFlow> = _sessionUI.asStateFlow() + + private val _sessionEvents: MutableSharedFlow = MutableSharedFlow() + val sessionEvent: SharedFlow + get() = _sessionEvents.asSharedFlow() + + init { + DappDelegate.wcEventModels + .filterNotNull() + .onEach { event -> + when (event) { + is Modal.Model.UpdatedSession -> { + _sessionUI.value = getSessions(event.topic) + } + + is Modal.Model.DeletedSession -> { + _sessionEvents.emit(DappSampleEvents.Disconnect) + } + + else -> Unit + } + }.launchIn(viewModelScope) + } + + private fun getSessions(topic: String? = null): List { + return (AppKit.getSession() as Session.WalletConnectSession).namespaces.values + .flatMap { it.accounts } + .filter { + val (chainNamespace, chainReference, account) = it.split(":") + val chain = Chains.values().find { chain -> + chain.chainNamespace == chainNamespace && chain.chainReference == chainReference + } + chain != null + } + .map { caip10Account -> + val (chainNamespace, chainReference, account) = caip10Account.split(":") + val chain = Chains.values().first() { chain -> + chain.chainNamespace == chainNamespace && chain.chainReference == chainReference + } + SessionUi(chain.icon, chain.name, account, chain.chainNamespace, chain.chainReference) + } + } + + fun ping() { + viewModelScope.launch { _sessionEvents.emit(DappSampleEvents.PingLoading) } + + try { + AppKit.ping(object : Modal.Listeners.SessionPing { + override fun onSuccess(pingSuccess: Modal.Model.Ping.Success) { + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.PingSuccess(pingSuccess.topic)) + } + } + + override fun onError(pingError: Modal.Model.Ping.Error) { + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.PingError) + } + } + }) + } catch (e: Exception) { + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.PingError) + } + } + } + + fun disconnect() { + try { + viewModelScope.launch { _sessionEvents.emit(DappSampleEvents.DisconnectLoading) } + AppKit.disconnect( + onSuccess = { + DappDelegate.deselectAccountDetails() + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.Disconnect) + } + }, + onError = { throwable: Throwable -> + Timber.tag(tag(this)).e(throwable.stackTraceToString()) + Firebase.crashlytics.recordException(throwable) + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.DisconnectError(throwable.message ?: "Unknown error, please try again or contact support")) + } + }) + + } catch (e: Exception) { + viewModelScope.launch { + _sessionEvents.emit(DappSampleEvents.DisconnectError(e.message ?: "Unknown error, please try again or contact support")) + } + } + } +} \ No newline at end of file diff --git a/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleHost.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleHost.kt new file mode 100644 index 000000000..eefa9c3ea --- /dev/null +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleHost.kt @@ -0,0 +1,149 @@ +@file:OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class) + +package com.reown.sample.dapp.ui.routes.host + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.Scaffold +import androidx.compose.material.ScaffoldState +import androidx.compose.material.Text +import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.compose.rememberNavController +import com.google.accompanist.navigation.material.BottomSheetNavigator +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.reown.appkit.client.AppKit +import com.reown.sample.dapp.R +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.sample.dapp.ui.DappSampleNavGraph +import com.reown.sample.dapp.ui.routes.Route +import kotlinx.coroutines.delay + +@Composable +fun DappSampleHost() { + val scaffoldState: ScaffoldState = rememberScaffoldState() + var isOfflineState: Boolean? by remember { mutableStateOf(null) } + val sheetState = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Hidden, + skipHalfExpanded = true + ) + + val bottomSheetNavigator = BottomSheetNavigator(sheetState) + val navController = rememberNavController(bottomSheetNavigator) + val viewModel: DappSampleViewModel = viewModel() + val isConnected = AppKit.getAccount() != null + val startDestination = if (isConnected) Route.Session.path else Route.ChainSelection.path + + LaunchedEffect(Unit) { + viewModel.events.collect { event -> + when (event) { + is DappSampleEvents.ConnectionEvent -> isOfflineState = !event.isAvailable + is DappSampleEvents.Disconnect -> navController.navigate(Route.ChainSelection.path) { + popUpTo(navController.graph.startDestinationId) { + inclusive = true + } + } + is DappSampleEvents.RequestError -> scaffoldState.snackbarHostState.showSnackbar(event.exceptionMsg) + is DappSampleEvents.SessionExtend -> scaffoldState.snackbarHostState.showSnackbar("Session extended") + else -> Unit + } + } + } + + Scaffold( + scaffoldState = scaffoldState + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) { + DappSampleNavGraph( + bottomSheetNavigator = bottomSheetNavigator, + navController = navController, + startDestination = startDestination, + ) + + if (isOfflineState != null) { + if (isOfflineState == true) { + NoConnectionIndicator() + } else { + RestoredConnectionIndicator() + } + } + } + } +} + +@Composable +private fun NoConnectionIndicator() { + var shouldShow by remember { mutableStateOf(true) } + + LaunchedEffect(key1 = Unit) { + delay(3000) + shouldShow = false + } + + if (shouldShow) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color(0xFF3496ff)) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) { + Text(text = "No internet connection", color = Color.White) + Spacer(modifier = Modifier.width(4.dp)) + Image( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_offline), + contentDescription = null, + modifier = Modifier.size(24.dp), + colorFilter = ColorFilter.tint(color = Color.White) + ) + } + } +} + +@Composable +private fun RestoredConnectionIndicator() { + var shouldShow by remember { mutableStateOf(true) } + + LaunchedEffect(key1 = Unit) { + delay(3000) + shouldShow = false + } + if (shouldShow) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color(0xFF93c47d)) + .padding(horizontal = 16.dp, vertical = 8.dp), + ) { + Text(text = "Network connection is OK", color = Color.White) + Spacer(modifier = Modifier.width(4.dp)) + Image( + imageVector = ImageVector.vectorResource(id = R.drawable.ic_white_check), + contentDescription = null, + modifier = Modifier.size(24.dp), + colorFilter = ColorFilter.tint(color = Color.White) + ) + } + } +} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleViewModel.kt b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleViewModel.kt similarity index 81% rename from sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleViewModel.kt rename to sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleViewModel.kt index 6f760e2ea..e1540d3c7 100644 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleViewModel.kt +++ b/sample/dapp/src/main/kotlin/com/reown/sample/dapp/ui/routes/host/DappSampleViewModel.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sample.dapp.ui.routes.host +package com.reown.sample.dapp.ui.routes.host import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.walletconnect.sample.dapp.domain.DappDelegate -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.wcmodal.client.Modal +import com.reown.sample.dapp.domain.DappDelegate +import com.reown.sample.dapp.ui.DappSampleEvents +import com.reown.appkit.client.Modal import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/DappSampleApp.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/DappSampleApp.kt deleted file mode 100644 index b40194d1d..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/DappSampleApp.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.walletconnect.sample.dapp - -import android.app.Application -import com.google.firebase.appdistribution.FirebaseAppDistribution -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.sample.common.tag -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import timber.log.Timber -import com.walletconnect.sample.common.BuildConfig as CommonBuildConfig - -class DappSampleApp : Application() { - - override fun onCreate() { - super.onCreate() - - val appMetaData = Core.Model.AppMetaData( - name = "Kotlin Dapp", - description = "Kotlin Dapp Implementation", - url = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app", - icons = listOf("https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"), - redirect = "kotlin-dapp-wc://request", - appLink = BuildConfig.DAPP_APP_LINK, - linkMode = true - ) - - CoreClient.initialize( - application = this, - projectId = CommonBuildConfig.PROJECT_ID, - metaData = appMetaData, - ) { - Firebase.crashlytics.recordException(it.throwable) - } - - WalletConnectModal.initialize( - Modal.Params.Init(core = CoreClient) - ) { error -> - Timber.e(tag(this), error.throwable.stackTraceToString()) - } - - FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() - } -} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/domain/DappDelegate.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/domain/DappDelegate.kt deleted file mode 100644 index 217846e8e..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/domain/DappDelegate.kt +++ /dev/null @@ -1,136 +0,0 @@ -package com.walletconnect.sample.dapp.domain - -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.sample.common.tag -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import timber.log.Timber - -object DappDelegate : WalletConnectModal.ModalDelegate, CoreClient.CoreDelegate { - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() - val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() - private val _coreEvents: MutableSharedFlow = MutableSharedFlow() - val coreEvents: SharedFlow = _coreEvents.asSharedFlow() - private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) - val connectionState: SharedFlow = _connectionState.asSharedFlow() - - var selectedSessionTopic: String? = null - private set - - init { - WalletConnectModal.setDelegate(this) - CoreClient.setDelegate(this) - } - - override fun onConnectionStateChange(state: Modal.Model.ConnectionState) { - Timber.d(tag(this), "onConnectionStateChange($state)") - scope.launch { - _connectionState.emit(state) - } - } - - override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { - selectedSessionTopic = approvedSession.topic - - scope.launch { - _wcEventModels.emit(approvedSession) - } - } - - override fun onSessionRejected(rejectedSession: Modal.Model.RejectedSession) { - scope.launch { - _wcEventModels.emit(rejectedSession) - } - } - - override fun onSessionUpdate(updatedSession: Modal.Model.UpdatedSession) { - scope.launch { - _wcEventModels.emit(updatedSession) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.SessionEvent) { - scope.launch { - _wcEventModels.emit(sessionEvent) - } - } - - override fun onSessionEvent(sessionEvent: Modal.Model.Event) { - scope.launch { - _wcEventModels.emit(sessionEvent) - } - } - - override fun onSessionDelete(deletedSession: Modal.Model.DeletedSession) { - deselectAccountDetails() - - scope.launch { - _wcEventModels.emit(deletedSession) - } - } - - override fun onSessionExtend(session: Modal.Model.Session) { - scope.launch { - _wcEventModels.emit(session) - } - } - - override fun onSessionRequestResponse(response: Modal.Model.SessionRequestResponse) { - scope.launch { - _wcEventModels.emit(response) - } - } - - override fun onSessionAuthenticateResponse(sessionUpdateResponse: Modal.Model.SessionAuthenticateResponse) { - if (sessionUpdateResponse is Modal.Model.SessionAuthenticateResponse.Result) { - selectedSessionTopic = sessionUpdateResponse.session?.topic - } - scope.launch { - _wcEventModels.emit(sessionUpdateResponse) - } - } - - override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) { - scope.launch { - _wcEventModels.emit(proposal) - } - } - - override fun onRequestExpired(request: Modal.Model.ExpiredRequest) { - scope.launch { - _wcEventModels.emit(request) - } - } - - fun deselectAccountDetails() { - selectedSessionTopic = null - } - - override fun onError(error: Modal.Model.Error) { - Timber.d(tag(this), error.throwable.stackTraceToString()) - scope.launch { - _wcEventModels.emit(error) - } - } - - override fun onPairingDelete(deletedPairing: Core.Model.DeletedPairing) { - //Deprecated - pairings are automatically deleted - } - - override fun onPairingExpired(expiredPairing: Core.Model.ExpiredPairing) { - //Deprecated - pairings are automatically expired - } - - override fun onPairingState(pairingState: Core.Model.PairingState) { - Timber.d(tag(this), "Dapp pairing state: ${pairingState.isPairingState}") - } -} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleNavGraph.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleNavGraph.kt deleted file mode 100644 index 9e4c6272d..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/DappSampleNavGraph.kt +++ /dev/null @@ -1,64 +0,0 @@ -@file:OptIn(ExperimentalMaterialNavigationApi::class) - -package com.walletconnect.sample.dapp.ui - -import android.annotation.SuppressLint -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import androidx.navigation.NavDeepLink -import androidx.navigation.NavHostController -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.navArgument -import com.google.accompanist.navigation.material.BottomSheetNavigator -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.google.accompanist.navigation.material.ModalBottomSheetLayout -import com.walletconnect.sample.dapp.ui.routes.Route -import com.walletconnect.sample.dapp.ui.routes.composable_routes.account.AccountRoute -import com.walletconnect.sample.dapp.ui.routes.composable_routes.chain_selection.ChainSelectionRoute -import com.walletconnect.sample.dapp.ui.routes.composable_routes.session.SessionRoute -import com.walletconnect.wcmodal.ui.walletConnectModalGraph - -@SuppressLint("RestrictedApi") -@Composable -fun DappSampleNavGraph( - bottomSheetNavigator: BottomSheetNavigator, - navController: NavHostController, - startDestination: String, -) { - ModalBottomSheetLayout( - bottomSheetNavigator = bottomSheetNavigator, - sheetBackgroundColor = Color.Transparent, - sheetElevation = 0.dp, - scrimColor = Color.Unspecified, - sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) - ) { - NavHost( - navController = navController, - startDestination = startDestination - ) { - composable(Route.ChainSelection.path) { - ChainSelectionRoute(navController) - } - composable(Route.Session.path, deepLinks = listOf(NavDeepLink("kotlin-dapp-wc://request"))) { - SessionRoute(navController) - } - composable( - route = Route.Account.path + "/{$accountArg}", - arguments = listOf(navArgument(accountArg) { type = NavType.StringType }) - ) { - AccountRoute(navController) - } - walletConnectModalGraph(navController) - } - } -} - -const val accountArg= "accountArg" -fun NavController.navigateToAccount(selectedAccount: String) { - navigate(Route.Account.path + "/$selectedAccount") -} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/Route.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/Route.kt deleted file mode 100644 index 13c18e06b..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/Route.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.walletconnect.sample.dapp.ui.routes - -sealed class Route(val path: String) { - object ChainSelection : Route("chain_selection") - object ParingGeneration : Route("paring_generation") - object Session : Route("session") - object Account : Route("account") -} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt deleted file mode 100644 index 3483caec2..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountRoute.kt +++ /dev/null @@ -1,167 +0,0 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.account - -import android.content.Intent -import android.net.Uri -import android.widget.Toast -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController -import coil.compose.AsyncImage -import com.walletconnect.sample.common.ui.commons.BlueButton -import com.walletconnect.sample.common.ui.commons.FullScreenLoader -import com.walletconnect.sample.common.ui.commons.Loader -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.sample.dapp.ui.routes.Route -import timber.log.Timber - -@Composable -fun AccountRoute(navController: NavController) { - val viewModel: AccountViewModel = viewModel() - val state by viewModel.uiState.collectAsState() - val awaitResponse by viewModel.awaitResponse.collectAsState(false) - val context = LocalContext.current - - LaunchedEffect(Unit) { - viewModel.fetchAccountDetails() - - viewModel.events.collect { event -> - when (event) { - is DappSampleEvents.RequestSuccess -> { - Toast.makeText(context, "Result: ${event.result}", Toast.LENGTH_LONG).show() - } - - is DappSampleEvents.RequestPeerError -> { - Toast.makeText(context, "Error: ${event.errorMsg}", Toast.LENGTH_LONG).show() - } - - is DappSampleEvents.RequestError -> { - Toast.makeText(context, "Error: ${event.exceptionMsg}", Toast.LENGTH_LONG).show() - } - - is DappSampleEvents.Disconnect -> navController.popBackStack(Route.ChainSelection.path, false) - else -> Unit - } - } - } - - AccountScreen( - state = state, - onMethodClick = viewModel::requestMethod, - awaitResponse = awaitResponse - ) -} - -@Composable -private fun AccountScreen( - state: AccountUi, - onMethodClick: (String, (Uri) -> Unit) -> Unit, - awaitResponse: Boolean, -) { - when (state) { - AccountUi.Loading -> FullScreenLoader() - is AccountUi.AccountData -> AccountContent(state, onMethodClick, awaitResponse) - } -} - -@Composable -fun AccountContent( - state: AccountUi.AccountData, - onMethodClick: (String, (Uri) -> Unit) -> Unit, - awaitResponse: Boolean, -) { - Box { - Column( - modifier = Modifier - .fillMaxSize() - .padding(20.dp) - ) { - ChainData(chain = state) - Spacer(modifier = Modifier.height(6.dp)) - MethodList( - methods = state.listOfMethods, - onMethodClick = onMethodClick - ) - } - - if (awaitResponse) { - Loader() - } - } -} - -@Composable -private fun ChainData(chain: AccountUi.AccountData) { - Column( - modifier = Modifier - .clickable { } - .fillMaxWidth() - .padding(horizontal = 24.dp, 16.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage(model = chain.icon, contentDescription = null, Modifier.size(48.dp)) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = chain.chainName, - style = TextStyle(fontWeight = FontWeight.SemiBold, fontSize = 18.sp) - ) - } - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = chain.account, - style = TextStyle(fontSize = 12.sp), - maxLines = 1, - modifier = Modifier.padding(horizontal = 6.dp), - overflow = TextOverflow.Ellipsis, - ) - } -} - -@Composable -private fun MethodList( - methods: List, - onMethodClick: (String, (Uri) -> Unit) -> Unit, -) { - val context = LocalContext.current - LazyColumn(modifier = Modifier.fillMaxWidth()) { - itemsIndexed(methods) { _, item -> - BlueButton( - text = item, - onClick = { - onMethodClick(item) { uri -> - try { - context.startActivity(Intent(Intent.ACTION_VIEW, uri)) - } catch (e: Exception) { - Timber.tag("AccountRoute").d("Activity not found: $e") - } - } - }, - modifier = Modifier.fillMaxWidth(), - ) - } - } -} diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt deleted file mode 100644 index 7c7a56b72..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/account/AccountViewModel.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.account - -import android.net.Uri -import androidx.core.net.toUri -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.walletconnect.sample.common.Chains -import com.walletconnect.sample.common.getEthSendTransaction -import com.walletconnect.sample.common.getEthSignBody -import com.walletconnect.sample.common.getEthSignTypedData -import com.walletconnect.sample.common.getPersonalSignBody -import com.walletconnect.sample.dapp.domain.DappDelegate -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.sample.dapp.ui.accountArg -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -class AccountViewModel( - savedStateHandle: SavedStateHandle -) : ViewModel() { - private val selectedAccountInfo = checkNotNull(savedStateHandle.get(accountArg)) - - private val _uiState: MutableStateFlow = MutableStateFlow(AccountUi.Loading) - val uiState: StateFlow = _uiState.asStateFlow() - - private val _awaitResponse: MutableStateFlow = MutableStateFlow(false) - val awaitResponse: StateFlow = _awaitResponse.asStateFlow() - - private val _events: MutableSharedFlow = MutableSharedFlow() - val events: SharedFlow - get() = _events.asSharedFlow() - - init { - DappDelegate.wcEventModels - .filterNotNull() - .onEach { walletEvent -> - when (walletEvent) { - is Modal.Model.UpdatedSession -> fetchAccountDetails() - is Modal.Model.SessionRequestResponse -> { - val request = when (walletEvent.result) { - is Modal.Model.JsonRpcResponse.JsonRpcResult -> { - _awaitResponse.value = false - val successResult = (walletEvent.result as Modal.Model.JsonRpcResponse.JsonRpcResult) - DappSampleEvents.RequestSuccess(successResult.result) - } - - is Modal.Model.JsonRpcResponse.JsonRpcError -> { - _awaitResponse.value = false - val errorResult = (walletEvent.result as Modal.Model.JsonRpcResponse.JsonRpcError) - DappSampleEvents.RequestPeerError("Error Message: ${errorResult.message}\n Error Code: ${errorResult.code}") - } - } - - _events.emit(request) - } - - is Modal.Model.ExpiredRequest -> { - _awaitResponse.value = false - _events.emit(DappSampleEvents.RequestError("Request expired")) - } - - is Modal.Model.DeletedSession -> { - _events.emit(DappSampleEvents.Disconnect) - } - - else -> Unit - } - } - .launchIn(viewModelScope) - } - - fun requestMethod(method: String, sendSessionRequestDeepLink: (Uri) -> Unit) { - (uiState.value as? AccountUi.AccountData)?.let { currentState -> - try { - _awaitResponse.value = true - - val (parentChain, chainId, account) = currentState.selectedAccount.split(":") - val params: String = when { - method.equals("personal_sign", true) -> getPersonalSignBody(account) - method.equals("eth_sign", true) -> getEthSignBody(account) - method.equals("eth_sendTransaction", true) -> getEthSendTransaction(account) - method.equals("eth_signTypedData", true) -> getEthSignTypedData(account) - else -> "[]" - } - val requestParams = Modal.Params.Request( - sessionTopic = requireNotNull(DappDelegate.selectedSessionTopic), - method = method, - params = params, // stringified JSON - chainId = "$parentChain:$chainId" - ) - - WalletConnectModal.request(requestParams, - onSuccess = { - WalletConnectModal.getActiveSessionByTopic(requestParams.sessionTopic)?.redirect?.toUri()?.let { deepLinkUri -> sendSessionRequestDeepLink(deepLinkUri) } - }, - onError = { - viewModelScope.launch { - _awaitResponse.value = false - _events.emit(DappSampleEvents.RequestError(it.throwable.localizedMessage ?: "Error trying to send request")) - } - }) - } catch (e: Exception) { - viewModelScope.launch { - _awaitResponse.value = false - _events.emit(DappSampleEvents.RequestError(e.localizedMessage ?: "Error trying to send request")) - } - } - } - } - - fun fetchAccountDetails() { - val (chainNamespace, chainReference, account) = selectedAccountInfo.split(":") - val chainDetails = Chains.values().first { - it.chainNamespace == chainNamespace && it.chainReference == chainReference - } - - val listOfMethodsByChainId: List = - WalletConnectModal.getListOfActiveSessions() - .filter { session -> session.topic == DappDelegate.selectedSessionTopic } - .flatMap { session -> - session.namespaces - .filter { (namespaceKey, _) -> namespaceKey == chainDetails.chainId } - .flatMap { (_, namespace) -> namespace.methods } - } - - val listOfMethodsByNamespace: List = - WalletConnectModal.getListOfActiveSessions() - .filter { session -> session.topic == DappDelegate.selectedSessionTopic } - .flatMap { session -> - session.namespaces - .filter { (namespaceKey, _) -> namespaceKey == chainDetails.chainNamespace } - .flatMap { (_, namespace) -> namespace.methods } - } - - - viewModelScope.launch { - _uiState.value = AccountUi.AccountData( - icon = chainDetails.icon, - chainName = chainDetails.chainName, - account = account, - listOfMethods = listOfMethodsByChainId.ifEmpty { listOfMethodsByNamespace }, - selectedAccount = selectedAccountInfo - ) - } - } -} \ No newline at end of file diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt deleted file mode 100644 index 33c71bb75..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionUi.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.session - -data class SessionUi( - val icon: Int, - val name: String, - val address: String, - val chainNamespace: String, - val chainReference: String, -) diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt deleted file mode 100644 index d7b3ab2f4..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/composable_routes/session/SessionViewModel.kt +++ /dev/null @@ -1,121 +0,0 @@ -package com.walletconnect.sample.dapp.ui.routes.composable_routes.session - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase -import com.walletconnect.sample.common.Chains -import com.walletconnect.sample.common.tag -import com.walletconnect.sample.dapp.domain.DappDelegate -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.wcmodal.client.Modal -import com.walletconnect.wcmodal.client.WalletConnectModal -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import timber.log.Timber - -class SessionViewModel : ViewModel() { - - private val _sessionUI: MutableStateFlow> = MutableStateFlow(getSessions()) - val uiState: StateFlow> = _sessionUI.asStateFlow() - - private val _sessionEvents: MutableSharedFlow = MutableSharedFlow() - val sessionEvent: SharedFlow - get() = _sessionEvents.asSharedFlow() - - init { - DappDelegate.wcEventModels - .filterNotNull() - .onEach { event -> - when (event) { - is Modal.Model.UpdatedSession -> { - _sessionUI.value = getSessions(event.topic) - } - - is Modal.Model.DeletedSession -> { - _sessionEvents.emit(DappSampleEvents.Disconnect) - } - - else -> Unit - } - }.launchIn(viewModelScope) - } - - private fun getSessions(topic: String? = null): List { - return WalletConnectModal.getListOfActiveSessions().filter { - if (topic != null) { - it.topic == topic - } else { - it.topic == DappDelegate.selectedSessionTopic - } - }.flatMap { settledSession -> settledSession.namespaces.values.flatMap { it.accounts } } - .map { caip10Account -> - val (chainNamespace, chainReference, account) = caip10Account.split(":") - val chain = Chains.values().first { chain -> - chain.chainNamespace == chainNamespace && chain.chainReference == chainReference - } - SessionUi(chain.icon, chain.name, account, chain.chainNamespace, chain.chainReference) - } - } - - fun ping() { - val pingParams = Modal.Params.Ping(topic = requireNotNull(DappDelegate.selectedSessionTopic)) - viewModelScope.launch { _sessionEvents.emit(DappSampleEvents.PingLoading) } - - try { - WalletConnectModal.ping(pingParams, object : Modal.Listeners.SessionPing { - override fun onSuccess(pingSuccess: Modal.Model.Ping.Success) { - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.PingSuccess(pingSuccess.topic)) - } - } - - override fun onError(pingError: Modal.Model.Ping.Error) { - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.PingError) - } - } - }) - } catch (e: Exception) { - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.PingError) - } - } - } - - fun disconnect() { - if (DappDelegate.selectedSessionTopic != null) { - try { - viewModelScope.launch { _sessionEvents.emit(DappSampleEvents.DisconnectLoading) } - val disconnectParams = Modal.Params.Disconnect(sessionTopic = requireNotNull(DappDelegate.selectedSessionTopic)) - WalletConnectModal.disconnect(disconnectParams, - onSuccess = { - DappDelegate.deselectAccountDetails() - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.Disconnect) - } - }, - onError = { error -> - Timber.tag(tag(this)).e(error.throwable.stackTraceToString()) - Firebase.crashlytics.recordException(error.throwable) - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.DisconnectError(error.throwable.message ?: "Unknown error, please try again or contact support")) - } - }) - - } catch (e: Exception) { - viewModelScope.launch { - _sessionEvents.emit(DappSampleEvents.DisconnectError(e.message ?: "Unknown error, please try again or contact support")) - } - } - } - } -} \ No newline at end of file diff --git a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleHost.kt b/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleHost.kt deleted file mode 100644 index 2b1074a2b..000000000 --- a/sample/dapp/src/main/kotlin/com/walletconnect/sample/dapp/ui/routes/host/DappSampleHost.kt +++ /dev/null @@ -1,132 +0,0 @@ -@file:OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class) - -package com.walletconnect.sample.dapp.ui.routes.host - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Scaffold -import androidx.compose.material.ScaffoldState -import androidx.compose.material.Text -import androidx.compose.material.rememberModalBottomSheetState -import androidx.compose.material.rememberScaffoldState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.compose.rememberNavController -import com.google.accompanist.navigation.material.BottomSheetNavigator -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.walletconnect.sample.dapp.R -import com.walletconnect.sample.dapp.ui.DappSampleEvents -import com.walletconnect.sample.dapp.ui.DappSampleNavGraph -import com.walletconnect.sample.dapp.ui.routes.Route -import kotlinx.coroutines.delay - -@Composable -fun DappSampleHost() { - val scaffoldState: ScaffoldState = rememberScaffoldState() - var isOfflineState: Boolean? by remember { mutableStateOf(null) } - val sheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - skipHalfExpanded = true - ) - val bottomSheetNavigator = BottomSheetNavigator(sheetState) - val navController = rememberNavController(bottomSheetNavigator) - val viewModel: DappSampleViewModel = viewModel() - - LaunchedEffect(Unit) { - viewModel.events.collect { event -> - when (event) { - is DappSampleEvents.ConnectionEvent -> isOfflineState = !event.isAvailable - is DappSampleEvents.Disconnect -> navController.navigate(Route.ChainSelection.path) - is DappSampleEvents.RequestError -> scaffoldState.snackbarHostState.showSnackbar(event.exceptionMsg) - is DappSampleEvents.SessionExtend -> scaffoldState.snackbarHostState.showSnackbar("Session extended") - else -> Unit - } - } - } - - Scaffold( - scaffoldState = scaffoldState - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - DappSampleNavGraph( - bottomSheetNavigator = bottomSheetNavigator, - navController = navController, - startDestination = Route.ChainSelection.path, - ) - - if (isOfflineState != null) { - if (isOfflineState == true) { - NoConnectionIndicator() - } else { - RestoredConnectionIndicator() - } - } - } - } -} - -@Composable -private fun NoConnectionIndicator() { - Row( - modifier = Modifier - .fillMaxWidth() - .background(Color(0xFF3496ff)) - .padding(horizontal = 16.dp, vertical = 8.dp), - ) { - Text(text = "No internet connection", color = Color.White) - Spacer(modifier = Modifier.width(4.dp)) - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_offline), - contentDescription = null, - modifier = Modifier.size(24.dp), - colorFilter = ColorFilter.tint(color = Color.White) - ) - } -} - -@Composable -private fun RestoredConnectionIndicator() { - var shouldShow by remember { mutableStateOf(true) } - - LaunchedEffect(key1 = Unit) { - delay(2000) - shouldShow = false - } - if (shouldShow) { - Row( - modifier = Modifier - .fillMaxWidth() - .background(Color(0xFF93c47d)) - .padding(horizontal = 16.dp, vertical = 8.dp), - ) { - Text(text = "Network connection is OK", color = Color.White) - Spacer(modifier = Modifier.width(4.dp)) - Image( - imageVector = ImageVector.vectorResource(id = R.drawable.ic_white_check), - contentDescription = null, - modifier = Modifier.size(24.dp), - colorFilter = ColorFilter.tint(color = Color.White) - ) - } - } -} diff --git a/sample/modal/build.gradle.kts b/sample/modal/build.gradle.kts index 197205a3b..ac9c78032 100644 --- a/sample/modal/build.gradle.kts +++ b/sample/modal/build.gradle.kts @@ -8,11 +8,11 @@ plugins { } android { - namespace = "com.walletconnect.sample.modal" + namespace = "com.reown.sample.modal" compileSdk = COMPILE_SDK defaultConfig { - applicationId = "com.walletconnect.sample.modal" + applicationId = "com.reown.sample.modal" minSdk = MIN_SDK targetSdk = TARGET_SDK versionName = SAMPLE_VERSION_NAME @@ -28,18 +28,18 @@ android { buildTypes { getByName("release") { manifestPlaceholders["pathPrefix"] = "/lab_release" - buildConfigField("String", "LAB_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/lab_release\"") + buildConfigField("String", "LAB_APP_LINK", "\"https://dev.lab.web3modal.com/lab_release\"") } getByName("internal") { manifestPlaceholders["pathPrefix"] = "/lab_internal" - buildConfigField("String", "LAB_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/lab_internal\"") + buildConfigField("String", "LAB_APP_LINK", "\"https://dev.lab.web3modal.com/lab_internal\"") } getByName("debug") { manifestPlaceholders["pathPrefix"] = "/lab_debug" - buildConfigField("String", "LAB_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/lab_debug\"") + buildConfigField("String", "LAB_APP_LINK", "\"https://dev.lab.web3modal.com/lab_debug\"") } } @@ -69,6 +69,7 @@ android { dependencies { implementation(project(":sample:common")) + implementation("androidx.compose.material:material-icons-core:1.7.1") implementation(platform(libs.firebase.bom)) implementation(libs.bundles.firebase) @@ -90,12 +91,12 @@ dependencies { api(libs.bundles.androidxNavigation) debugImplementation(project(":core:android")) - debugImplementation(project(":product:web3modal")) + debugImplementation(project(":product:appkit")) internalImplementation(project(":core:android")) - internalImplementation(project(":product:web3modal")) + internalImplementation(project(":product:appkit")) - releaseImplementation(platform("com.walletconnect:android-bom:$BOM_VERSION")) - releaseImplementation("com.walletconnect:android-core") - releaseImplementation("com.walletconnect:web3modal") + releaseImplementation(platform("com.reown:android-bom:$BOM_VERSION")) + releaseImplementation("com.reown:android-core") + releaseImplementation("com.reown:appkit") } \ No newline at end of file diff --git a/sample/modal/src/main/AndroidManifest.xml b/sample/modal/src/main/AndroidManifest.xml index 5afc62b49..34a43b4e0 100644 --- a/sample/modal/src/main/AndroidManifest.xml +++ b/sample/modal/src/main/AndroidManifest.xml @@ -44,16 +44,16 @@ - - - + + + diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/AppKitApp.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/AppKitApp.kt new file mode 100644 index 000000000..185be13f8 --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/AppKitApp.kt @@ -0,0 +1,62 @@ +package com.reown.sample.modal + +import android.app.Application +import com.google.firebase.appdistribution.FirebaseAppDistribution +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.relay.ConnectionType +import com.reown.sample.common.tag +import com.reown.util.bytesToHex +import com.reown.util.randomBytes +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.presets.AppKitChainsPresets +import com.reown.appkit.utils.EthUtils +import timber.log.Timber +import com.reown.sample.common.BuildConfig as CommonBuildConfig + +class AppKitApp : Application() { + override fun onCreate() { + super.onCreate() + + val appMetaData = Core.Model.AppMetaData( + name = "AppKit Lab Sample", + description = "Kotlin AppKit Lab Sample", + url = "https://dev.lab.web3modal.com", + icons = listOf("https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"), + redirect = "kotlin-modal-wc://request", + linkMode = true, + appLink = BuildConfig.LAB_APP_LINK + ) + + CoreClient.initialize( + projectId = CommonBuildConfig.PROJECT_ID, + connectionType = ConnectionType.AUTOMATIC, + application = this, + metaData = appMetaData, + ) { + Timber.e(it.throwable) + } + + AppKit.initialize(Modal.Params.Init(core = CoreClient)) { error -> + Timber.e(tag(this), error.throwable.stackTraceToString()) + Firebase.crashlytics.recordException(error.throwable) + } + + AppKit.setChains(AppKitChainsPresets.ethChains.values.toList()) + + val authParams = Modal.Model.AuthPayloadParams( + chains = AppKitChainsPresets.ethChains.values.toList().map { it.id }, + domain = "sample.kotlin.modal", + uri = "https://web3inbox.com/all-apps", + nonce = randomBytes(12).bytesToHex(), + statement = "I accept the Terms of Service: https://yourDappDomain.com/", + methods = EthUtils.ethMethods + ) + AppKit.setAuthRequestParams(authParams) + + FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() + } +} diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/MainActivity.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/MainActivity.kt similarity index 82% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/MainActivity.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/MainActivity.kt index f5eb0fac0..df0491a4f 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/MainActivity.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/MainActivity.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialNavigationApi::class, ExperimentalMaterialApi::class) -package com.walletconnect.sample.modal +package com.reown.sample.modal import android.content.Intent import android.os.Bundle @@ -61,24 +61,24 @@ import androidx.navigation.navArgument import com.google.accompanist.navigation.material.BottomSheetNavigator import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi import com.google.accompanist.navigation.material.ModalBottomSheetLayout -import com.walletconnect.sample.modal.common.Route -import com.walletconnect.sample.modal.common.messageArg -import com.walletconnect.sample.modal.compose.ComposeActivity -import com.walletconnect.sample.modal.kotlindsl.KotlinDSLActivity -import com.walletconnect.sample.modal.navComponent.NavComponentActivity -import com.walletconnect.sample.modal.ui.AlertDialogRoute -import com.walletconnect.sample.modal.ui.LabScreen -import com.walletconnect.sample.modal.ui.predefinedGreenDarkTheme -import com.walletconnect.sample.modal.ui.predefinedGreenLightTheme -import com.walletconnect.sample.modal.ui.predefinedOrangeDarkTheme -import com.walletconnect.sample.modal.ui.predefinedOrangeLightTheme -import com.walletconnect.sample.modal.ui.predefinedRedDarkTheme -import com.walletconnect.sample.modal.ui.predefinedRedLightTheme -import com.walletconnect.sample.modal.ui.theme.WalletConnectTheme -import com.walletconnect.sample.modal.view.ViewActivity -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.ui.Web3ModalTheme -import com.walletconnect.web3.modal.ui.web3ModalGraph +import com.reown.sample.modal.common.Route +import com.reown.sample.modal.common.messageArg +import com.reown.sample.modal.compose.ComposeActivity +import com.reown.sample.modal.kotlindsl.KotlinDSLActivity +import com.reown.sample.modal.navComponent.NavComponentActivity +import com.reown.sample.modal.ui.AlertDialogRoute +import com.reown.sample.modal.ui.LabScreen +import com.reown.sample.modal.ui.predefinedGreenDarkTheme +import com.reown.sample.modal.ui.predefinedGreenLightTheme +import com.reown.sample.modal.ui.predefinedOrangeDarkTheme +import com.reown.sample.modal.ui.predefinedOrangeLightTheme +import com.reown.sample.modal.ui.predefinedRedDarkTheme +import com.reown.sample.modal.ui.predefinedRedLightTheme +import com.reown.sample.modal.ui.theme.WalletConnectTheme +import com.reown.sample.modal.view.ViewActivity +import com.reown.appkit.client.AppKit +import com.reown.appkit.ui.AppKitTheme +import com.reown.appkit.ui.appKitGraph import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -93,14 +93,14 @@ class MainActivity : ComponentActivity() { var counter = 10 while (!isRegistered && counter-- > 0) { try { - Web3Modal.register(this) + AppKit.register(this) isRegistered = true } catch (e: Exception) { Timber.e(e) runBlocking { delay(100) } } } - if (counter <= 0) throw IllegalStateException("Web3Modal registration failed") + if (counter <= 0) throw IllegalStateException("AppKit registration failed") setContent { WalletConnectTheme { @@ -114,10 +114,10 @@ class MainActivity : ComponentActivity() { val isDarkTheme = isSystemInDarkTheme() var isDark by remember { mutableStateOf(isDarkTheme) } - var darkColors by remember { mutableStateOf(Web3ModalTheme.provideDarkWeb3ModalColor()) } - var lightColors by remember { mutableStateOf(Web3ModalTheme.provideLightWeb3ModalColors()) } - Web3ModalTheme( - mode = if (isDark) Web3ModalTheme.Mode.DARK else Web3ModalTheme.Mode.LIGHT, + var darkColors by remember { mutableStateOf(AppKitTheme.provideDarkAppKitColor()) } + var lightColors by remember { mutableStateOf(AppKitTheme.provideLightAppKitColors()) } + AppKitTheme( + mode = if (isDark) AppKitTheme.Mode.DARK else AppKitTheme.Mode.LIGHT, lightColors = lightColors, darkColors = darkColors ) { @@ -132,7 +132,7 @@ class MainActivity : ComponentActivity() { TopAppBar( title = { Text( - text = "Web3Modal Lab", + text = "AppKit Lab", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) @@ -160,7 +160,7 @@ class MainActivity : ComponentActivity() { composable(Route.Lab.path) { LabScreen(navController = navController) } - web3ModalGraph(navController) + appKitGraph(navController) dialog( route = Route.AlertDialog.path + "/{$messageArg}", arguments = listOf(navArgument(messageArg) { type = NavType.StringType }), @@ -178,7 +178,7 @@ class MainActivity : ComponentActivity() { } if (intent?.dataString?.contains("wc_ev") == true) { - Web3Modal.handleDeepLink(intent.dataString ?: "") { + AppKit.handleDeepLink(intent.dataString ?: "") { lifecycleScope.launch(Dispatchers.Main) { Toast.makeText(this@MainActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() } @@ -190,7 +190,7 @@ class MainActivity : ComponentActivity() { super.onNewIntent(intent) if (intent?.dataString?.contains("wc_ev") == true) { - Web3Modal.handleDeepLink(intent.dataString ?: "") { + AppKit.handleDeepLink(intent.dataString ?: "") { lifecycleScope.launch(Dispatchers.Main) { Toast.makeText(this@MainActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() } @@ -200,13 +200,13 @@ class MainActivity : ComponentActivity() { override fun onDestroy() { super.onDestroy() - Web3Modal.unregister() + AppKit.unregister() } } @Composable private fun PredefineThemes( - onClick: (Web3ModalTheme.Colors, Web3ModalTheme.Colors) -> Unit, + onClick: (AppKitTheme.Colors, AppKitTheme.Colors) -> Unit, ) { LazyRow( modifier = Modifier @@ -214,7 +214,7 @@ private fun PredefineThemes( .padding(horizontal = 18.dp, vertical = 4.dp), horizontalArrangement = Arrangement.Center ) { - item { ThemeItem(color = Color(0xFF47A1FF), text = "Default") { onClick(Web3ModalTheme.provideLightWeb3ModalColors(), Web3ModalTheme.provideDarkWeb3ModalColor()) } } + item { ThemeItem(color = Color(0xFF47A1FF), text = "Default") { onClick(AppKitTheme.provideLightAppKitColors(), AppKitTheme.provideDarkAppKitColor()) } } item { ThemeItem(color = Color(0xFFFFA500), text = "Orange") { onClick(predefinedOrangeLightTheme, predefinedOrangeDarkTheme) } } item { ThemeItem(color = Color(0xFFB7342B), text = "Red") { onClick(predefinedRedLightTheme, predefinedRedDarkTheme) } } item { ThemeItem(color = Color(0xFF10B124), text = "Green") { onClick(predefinedGreenLightTheme, predefinedGreenDarkTheme) } } @@ -257,10 +257,7 @@ private fun ThemeModeIcon( modifier = Modifier .padding(12.dp) .size(20.dp) - .clickable( - indication = rememberRipple(bounded = false), - interactionSource = remember { MutableInteractionSource() }, - ) { onClick(!isDarkTheme) } + .clickable { onClick(!isDarkTheme) } ) } diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleDelegate.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/ModalSampleDelegate.kt similarity index 91% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleDelegate.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/ModalSampleDelegate.kt index eef293b55..c0eaeb5a0 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleDelegate.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/ModalSampleDelegate.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sample.modal +package com.reown.sample.modal -import com.walletconnect.sample.common.tag -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal +import com.reown.sample.common.tag +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -12,14 +12,14 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import timber.log.Timber -object ModalSampleDelegate : Web3Modal.ModalDelegate { +object ModalSampleDelegate : AppKit.ModalDelegate { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val _wcEventModels: MutableSharedFlow = MutableSharedFlow() val wcEventModels: SharedFlow = _wcEventModels.asSharedFlow() init { - Web3Modal.setDelegate(this) + AppKit.setDelegate(this) } override fun onSessionApproved(approvedSession: Modal.Model.ApprovedSession) { diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/common/Route.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/common/Route.kt new file mode 100644 index 000000000..3275a9dc1 --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/common/Route.kt @@ -0,0 +1,11 @@ +package com.reown.sample.modal.common + +const val messageArg = "messageArg" + +sealed class Route(val path: String) { + object Home : Route("Home") + + object Lab : Route("Lab") + + object AlertDialog : Route("Alert") +} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/ComposeActivity.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/compose/ComposeActivity.kt similarity index 91% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/ComposeActivity.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/compose/ComposeActivity.kt index e26042b0c..bc0d83927 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/ComposeActivity.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/compose/ComposeActivity.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialNavigationApi::class, ExperimentalMaterialApi::class) -package com.walletconnect.sample.modal.compose +package com.reown.sample.modal.compose import android.os.Bundle import androidx.activity.ComponentActivity @@ -27,9 +27,9 @@ import androidx.navigation.compose.rememberNavController import com.google.accompanist.navigation.material.BottomSheetNavigator import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi import com.google.accompanist.navigation.material.ModalBottomSheetLayout -import com.walletconnect.sample.modal.common.Route -import com.walletconnect.sample.modal.ui.theme.WalletConnectTheme -import com.walletconnect.web3.modal.ui.web3ModalGraph +import com.reown.sample.modal.common.Route +import com.reown.sample.modal.ui.theme.WalletConnectTheme +import com.reown.appkit.ui.appKitGraph class ComposeActivity : ComponentActivity() { @@ -66,7 +66,7 @@ class ComposeActivity : ComponentActivity() { composable(Route.Home.path) { HomeScreen(navController = navController) } - web3ModalGraph(navController) + appKitGraph(navController) } } } diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/compose/HomeScreen.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/compose/HomeScreen.kt new file mode 100644 index 000000000..3b7ff7d1b --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/compose/HomeScreen.kt @@ -0,0 +1,41 @@ +package com.reown.sample.modal.compose + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.reown.appkit.ui.components.button.AccountButton +import com.reown.appkit.ui.components.button.AccountButtonType +import com.reown.appkit.ui.components.button.ConnectButton +import com.reown.appkit.ui.components.button.ConnectButtonSize +import com.reown.appkit.ui.components.button.NetworkButton +import com.reown.appkit.ui.components.button.Web3Button +import com.reown.appkit.ui.components.button.rememberAppKitState + +@Composable +fun HomeScreen(navController: NavController) { + val web3ModalState = rememberAppKitState(navController = navController) + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + ConnectButton(state = web3ModalState, buttonSize = ConnectButtonSize.NORMAL) + Spacer(modifier = Modifier.height(20.dp)) + ConnectButton(state = web3ModalState, buttonSize = ConnectButtonSize.SMALL) + Spacer(modifier = Modifier.height(20.dp)) + AccountButton(web3ModalState, AccountButtonType.NORMAL) + Spacer(modifier = Modifier.height(20.dp)) + AccountButton(web3ModalState, AccountButtonType.MIXED) + Spacer(modifier = Modifier.height(20.dp)) + Web3Button(state = web3ModalState) + Spacer(modifier = Modifier.height(20.dp)) + NetworkButton(state = web3ModalState) + } +} diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/HomeFragment.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/HomeFragment.kt new file mode 100644 index 000000000..ed7e7d391 --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/HomeFragment.kt @@ -0,0 +1,17 @@ +package com.reown.sample.modal.kotlindsl + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import com.reown.sample.modal.R +import com.reown.sample.modal.databinding.FragmentHomeBinding +import com.reown.sample.common.viewBinding + +class HomeFragment : Fragment(R.layout.fragment_home) { + + private val binding by viewBinding(FragmentHomeBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/KotlinDSLActivity.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/KotlinDSLActivity.kt similarity index 77% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/KotlinDSLActivity.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/KotlinDSLActivity.kt index f1e761eb2..d094df083 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/KotlinDSLActivity.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/kotlindsl/KotlinDSLActivity.kt @@ -1,13 +1,13 @@ -package com.walletconnect.sample.modal.kotlindsl +package com.reown.sample.modal.kotlindsl import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.navigation.createGraph import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.fragment -import com.walletconnect.sample.modal.common.Route -import com.walletconnect.sample.modal.R -import com.walletconnect.web3.modal.ui.web3Modal +import com.reown.sample.modal.common.Route +import com.reown.sample.modal.R +import com.reown.appkit.ui.appKit class KotlinDSLActivity : AppCompatActivity(R.layout.activity_kotlin_dsl) { @@ -20,7 +20,7 @@ class KotlinDSLActivity : AppCompatActivity(R.layout.activity_kotlin_dsl) { startDestination = Route.Home.path ) { fragment(Route.Home.path) - web3Modal() + appKit() } } } \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/HomeFragment.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/HomeFragment.kt new file mode 100644 index 000000000..a7b711e05 --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/HomeFragment.kt @@ -0,0 +1,17 @@ +package com.reown.sample.modal.navComponent + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import com.reown.sample.modal.R +import com.reown.sample.modal.databinding.FragmentHomeBinding +import com.reown.sample.common.viewBinding + +class HomeFragment : Fragment(R.layout.fragment_home) { + + private val binding by viewBinding(FragmentHomeBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/NavComponentActivity.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/NavComponentActivity.kt similarity index 83% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/NavComponentActivity.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/NavComponentActivity.kt index a6214a5df..d01d917b2 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/NavComponentActivity.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/navComponent/NavComponentActivity.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sample.modal.navComponent +package com.reown.sample.modal.navComponent import androidx.appcompat.app.AppCompatActivity import androidx.navigation.fragment.NavHostFragment -import com.walletconnect.sample.modal.R +import com.reown.sample.modal.R class NavComponentActivity: AppCompatActivity(R.layout.activity_nav_component) { diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/AlertDialog.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/AlertDialog.kt similarity index 97% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/AlertDialog.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/ui/AlertDialog.kt index a3ad56002..7cea0af9d 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/AlertDialog.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/AlertDialog.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.modal.ui +package com.reown.sample.modal.ui import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/LabScreen.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/LabScreen.kt similarity index 77% rename from sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/LabScreen.kt rename to sample/modal/src/main/kotlin/com/reown/sample/modal/ui/LabScreen.kt index 366bcb1b7..eab1660f5 100644 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/LabScreen.kt +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/LabScreen.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.modal.ui +package com.reown.sample.modal.ui import android.widget.Toast import androidx.compose.foundation.layout.Arrangement @@ -14,20 +14,20 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.walletconnect.sample.common.getEthSendTransaction -import com.walletconnect.sample.common.getEthSignTypedData -import com.walletconnect.sample.common.getPersonalSignBody -import com.walletconnect.sample.common.ui.commons.BlueButton -import com.walletconnect.sample.modal.ModalSampleDelegate -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.client.models.request.Request -import com.walletconnect.web3.modal.client.models.request.SentRequestResult -import com.walletconnect.web3.modal.ui.Web3ModalTheme -import com.walletconnect.web3.modal.ui.components.button.AccountButtonType -import com.walletconnect.web3.modal.ui.components.button.NetworkButton -import com.walletconnect.web3.modal.ui.components.button.Web3Button -import com.walletconnect.web3.modal.ui.components.button.rememberWeb3ModalState +import com.reown.sample.common.getEthSendTransaction +import com.reown.sample.common.getEthSignTypedData +import com.reown.sample.common.getPersonalSignBody +import com.reown.sample.common.ui.commons.BlueButton +import com.reown.sample.modal.ModalSampleDelegate +import com.reown.appkit.client.Modal +import com.reown.appkit.client.AppKit +import com.reown.appkit.client.models.request.Request +import com.reown.appkit.client.models.request.SentRequestResult +import com.reown.appkit.ui.AppKitTheme +import com.reown.appkit.ui.components.button.AccountButtonType +import com.reown.appkit.ui.components.button.NetworkButton +import com.reown.appkit.ui.components.button.Web3Button +import com.reown.appkit.ui.components.button.rememberAppKitState import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -35,7 +35,7 @@ import kotlinx.coroutines.launch fun LabScreen( navController: NavController ) { - val web3ModalState = rememberWeb3ModalState(navController = navController) + val web3ModalState = rememberAppKitState(navController = navController) val isConnected by web3ModalState.isConnected.collectAsState() val coroutineScope = rememberCoroutineScope() @@ -62,8 +62,8 @@ fun LabScreen( } } - Web3ModalTheme( - mode = Web3ModalTheme.Mode.AUTO + AppKitTheme( + mode = AppKitTheme.Mode.AUTO ) { LazyColumn( modifier = Modifier.fillMaxSize(), @@ -73,7 +73,7 @@ fun LabScreen( item { Web3Button(state = web3ModalState, accountButtonType = AccountButtonType.MIXED) } item { NetworkButton(state = web3ModalState) } if (isConnected) { - Web3Modal.getAccount()?.let { session -> + AppKit.getAccount()?.let { session -> val account = session.address val onError: (Throwable) -> Unit = { coroutineScope.launch(Dispatchers.Main) { @@ -94,7 +94,7 @@ private fun sendPersonalSignRequest( onSuccess: (SentRequestResult) -> Unit, onError: (Throwable) -> Unit ) { - Web3Modal.request( + AppKit.request( request = Request("personal_sign", getPersonalSignBody(account)), onSuccess = onSuccess, onError = onError, @@ -106,7 +106,7 @@ private fun sendEthSendTransactionRequest( onSuccess: (SentRequestResult) -> Unit, onError: (Throwable) -> Unit ) { - Web3Modal.request( + AppKit.request( request = Request("eth_sendTransaction", getEthSendTransaction(account)), onSuccess = onSuccess, onError = onError, @@ -118,7 +118,7 @@ private fun sendEthSignTypedDataRequest( onSuccess: (SentRequestResult) -> Unit, onError: (Throwable) -> Unit ) { - Web3Modal.request( + AppKit.request( request = Request("eth_signTypedData", getEthSignTypedData(account)), onSuccess = onSuccess, onError = onError, diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/PredifinedThemes.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/PredifinedThemes.kt new file mode 100644 index 000000000..ad7742375 --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/PredifinedThemes.kt @@ -0,0 +1,43 @@ +package com.reown.sample.modal.ui + +import androidx.compose.ui.graphics.Color +import com.reown.appkit.ui.AppKitTheme + +val predefinedOrangeLightTheme = AppKitTheme.provideLightAppKitColors( + accent100 = Color(0xFFFFA500), + accent90 = Color(0xFFC7912F), + accent80 = Color(0xFFA5864E), + foreground = AppKitTheme.provideForegroundLightColorPalette(color100 = Color(0xFFBE7B00)) +) +val predefinedOrangeDarkTheme = AppKitTheme.provideDarkAppKitColor( + accent100 = Color(0xFFFFA500), + accent90 = Color(0xFFC7912F), + accent80 = Color(0xFFA5864E), + foreground = AppKitTheme.provideForegroundDarkColorPalette(color100 = Color(0xFFFFA500)) +) + +val predefinedRedLightTheme = AppKitTheme.provideLightAppKitColors( + accent100 = Color(0xFFB7342B), + accent90 = Color(0xFFA54740), + accent80 = Color(0xFF94504B), + background = AppKitTheme.provideBackgroundLightColorPalette(color100 = Color(0xFFFFCECA)) +) +val predefinedRedDarkTheme = AppKitTheme.provideDarkAppKitColor( + accent100 = Color(0xFFB7342B), + accent90 = Color(0xFFA54740), + accent80 = Color(0xFF94504B), + background = AppKitTheme.provideBackgroundDarkColorPalette(color100 = Color(0xFF350400)) +) + +val predefinedGreenLightTheme = AppKitTheme.provideLightAppKitColors( + accent100 = Color(0xFF10B124), + accent90 = Color(0xFF31AD41), + accent80 = Color(0xFF3B7242), + overlay = Color(0xFF10B124) +) +val predefinedGreenDarkTheme = AppKitTheme.provideDarkAppKitColor( + accent100 = Color(0xFF10B124), + accent90 = Color(0xFF31AD41), + accent80 = Color(0xFF3B7242), + overlay = Color(0xFF10B124) +) \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/theme/Theme.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/theme/Theme.kt new file mode 100644 index 000000000..6b8f7e9ce --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/ui/theme/Theme.kt @@ -0,0 +1,54 @@ +package com.reown.sample.modal.ui.theme + +import android.app.Activity +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +internal val darkColorScheme = darkColors( + primary = Color(0xFF47A1FF), + secondary = Color.White, + background = Color(0xFF141414), + onPrimary = Color.White +) + +internal val lightColorScheme = lightColors( + primary = Color(0xFF47A1FF), + secondary = Color(0xFF141414), + background = Color.White, + onPrimary = Color(0xFF141414) +) + +@Composable +fun WalletConnectTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colorScheme = if (darkTheme) darkColorScheme else lightColorScheme + + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colors = colorScheme, + content = content + ) +} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/reown/sample/modal/view/ViewActivity.kt b/sample/modal/src/main/kotlin/com/reown/sample/modal/view/ViewActivity.kt new file mode 100644 index 000000000..f04c253ab --- /dev/null +++ b/sample/modal/src/main/kotlin/com/reown/sample/modal/view/ViewActivity.kt @@ -0,0 +1,17 @@ +package com.reown.sample.modal.view + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.reown.sample.modal.R +import com.reown.appkit.ui.AppKitView + +class ViewActivity: AppCompatActivity(R.layout.activity_view) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val view = findViewById(R.id.web3Modal) + + view.setOnCloseModal { finish() } + } + +} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleApp.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleApp.kt deleted file mode 100644 index 6a1f32ef7..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ModalSampleApp.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.walletconnect.sample.modal - -import android.app.Application -import com.google.firebase.appdistribution.FirebaseAppDistribution -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.relay.ConnectionType -import com.walletconnect.sample.common.tag -import com.walletconnect.util.bytesToHex -import com.walletconnect.util.randomBytes -import com.walletconnect.web3.modal.client.Modal -import com.walletconnect.web3.modal.client.Web3Modal -import com.walletconnect.web3.modal.presets.Web3ModalChainsPresets -import com.walletconnect.web3.modal.utils.EthUtils -import timber.log.Timber -import com.walletconnect.sample.common.BuildConfig as CommonBuildConfig - -class ModalSampleApp : Application() { - override fun onCreate() { - super.onCreate() - - val appMetaData = Core.Model.AppMetaData( - name = "Kotlin Modals", - description = "Kotlin Modals Lab Sample", - url = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app", - icons = listOf("https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media"), - redirect = "kotlin-modal-wc://request", - linkMode = true, - appLink = BuildConfig.LAB_APP_LINK - ) - - CoreClient.initialize( - projectId = CommonBuildConfig.PROJECT_ID, - connectionType = ConnectionType.AUTOMATIC, - application = this, - metaData = appMetaData, - ) { - Timber.e(it.throwable) - } - - Web3Modal.initialize(Modal.Params.Init(core = CoreClient)) { error -> - Timber.e(tag(this), error.throwable.stackTraceToString()) - Firebase.crashlytics.recordException(error.throwable) - } - - Web3Modal.setChains(Web3ModalChainsPresets.ethChains.values.toList()) - - val authParams = Modal.Model.AuthPayloadParams( - chains = Web3ModalChainsPresets.ethChains.values.toList().map { it.id }, - domain = "sample.kotlin.modal", - uri = "https://web3inbox.com/all-apps", - nonce = randomBytes(12).bytesToHex(), - statement = "I accept the Terms of Service: https://yourDappDomain.com/", - methods = EthUtils.ethMethods - ) - Web3Modal.setAuthRequestParams(authParams) - - FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() - } -} diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/common/Route.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/common/Route.kt deleted file mode 100644 index f07a7384f..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/common/Route.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.sample.modal.common - -const val messageArg = "messageArg" - -sealed class Route(val path: String) { - object Home : Route("Home") - - object Lab : Route("Lab") - - object AlertDialog : Route("Alert") -} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/HomeScreen.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/HomeScreen.kt deleted file mode 100644 index b6a1534b5..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/compose/HomeScreen.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.walletconnect.sample.modal.compose - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.walletconnect.web3.modal.ui.components.button.AccountButton -import com.walletconnect.web3.modal.ui.components.button.AccountButtonType -import com.walletconnect.web3.modal.ui.components.button.ConnectButton -import com.walletconnect.web3.modal.ui.components.button.ConnectButtonSize -import com.walletconnect.web3.modal.ui.components.button.NetworkButton -import com.walletconnect.web3.modal.ui.components.button.Web3Button -import com.walletconnect.web3.modal.ui.components.button.rememberWeb3ModalState - -@Composable -fun HomeScreen(navController: NavController) { - val web3ModalState = rememberWeb3ModalState(navController = navController) - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - ConnectButton(state = web3ModalState, buttonSize = ConnectButtonSize.NORMAL) - Spacer(modifier = Modifier.height(20.dp)) - ConnectButton(state = web3ModalState, buttonSize = ConnectButtonSize.SMALL) - Spacer(modifier = Modifier.height(20.dp)) - AccountButton(web3ModalState, AccountButtonType.NORMAL) - Spacer(modifier = Modifier.height(20.dp)) - AccountButton(web3ModalState, AccountButtonType.MIXED) - Spacer(modifier = Modifier.height(20.dp)) - Web3Button(state = web3ModalState) - Spacer(modifier = Modifier.height(20.dp)) - NetworkButton(state = web3ModalState) - } -} diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/HomeFragment.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/HomeFragment.kt deleted file mode 100644 index 86e91dd4d..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/kotlindsl/HomeFragment.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.sample.modal.kotlindsl - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import com.walletconnect.sample.modal.R -import com.walletconnect.sample.modal.databinding.FragmentHomeBinding -import com.walletconnect.sample.common.viewBinding - -class HomeFragment : Fragment(R.layout.fragment_home) { - - private val binding by viewBinding(FragmentHomeBinding::bind) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - } -} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/HomeFragment.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/HomeFragment.kt deleted file mode 100644 index 281266843..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/navComponent/HomeFragment.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.sample.modal.navComponent - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import com.walletconnect.sample.modal.R -import com.walletconnect.sample.modal.databinding.FragmentHomeBinding -import com.walletconnect.sample.common.viewBinding - -class HomeFragment : Fragment(R.layout.fragment_home) { - - private val binding by viewBinding(FragmentHomeBinding::bind) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - } -} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/PredifinedThemes.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/PredifinedThemes.kt deleted file mode 100644 index fbd5d59a6..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/PredifinedThemes.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.walletconnect.sample.modal.ui - -import androidx.compose.ui.graphics.Color -import com.walletconnect.web3.modal.ui.Web3ModalTheme - -val predefinedOrangeLightTheme = Web3ModalTheme.provideLightWeb3ModalColors( - accent100 = Color(0xFFFFA500), - accent90 = Color(0xFFC7912F), - accent80 = Color(0xFFA5864E), - foreground = Web3ModalTheme.provideForegroundLightColorPalette(color100 = Color(0xFFBE7B00)) -) -val predefinedOrangeDarkTheme = Web3ModalTheme.provideDarkWeb3ModalColor( - accent100 = Color(0xFFFFA500), - accent90 = Color(0xFFC7912F), - accent80 = Color(0xFFA5864E), - foreground = Web3ModalTheme.provideForegroundDarkColorPalette(color100 = Color(0xFFFFA500)) -) - -val predefinedRedLightTheme = Web3ModalTheme.provideLightWeb3ModalColors( - accent100 = Color(0xFFB7342B), - accent90 = Color(0xFFA54740), - accent80 = Color(0xFF94504B), - background = Web3ModalTheme.provideBackgroundLightColorPalette(color100 = Color(0xFFFFCECA)) -) -val predefinedRedDarkTheme = Web3ModalTheme.provideDarkWeb3ModalColor( - accent100 = Color(0xFFB7342B), - accent90 = Color(0xFFA54740), - accent80 = Color(0xFF94504B), - background = Web3ModalTheme.provideBackgroundDarkColorPalette(color100 = Color(0xFF350400)) -) - -val predefinedGreenLightTheme = Web3ModalTheme.provideLightWeb3ModalColors( - accent100 = Color(0xFF10B124), - accent90 = Color(0xFF31AD41), - accent80 = Color(0xFF3B7242), - overlay = Color(0xFF10B124) -) -val predefinedGreenDarkTheme = Web3ModalTheme.provideDarkWeb3ModalColor( - accent100 = Color(0xFF10B124), - accent90 = Color(0xFF31AD41), - accent80 = Color(0xFF3B7242), - overlay = Color(0xFF10B124) -) \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/theme/Theme.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/theme/Theme.kt deleted file mode 100644 index ca96f22e0..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/ui/theme/Theme.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.walletconnect.sample.modal.ui.theme - -import android.app.Activity -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalView -import androidx.core.view.WindowCompat - -internal val darkColorScheme = darkColors( - primary = Color(0xFF47A1FF), - secondary = Color.White, - background = Color(0xFF141414), - onPrimary = Color.White -) - -internal val lightColorScheme = lightColors( - primary = Color(0xFF47A1FF), - secondary = Color(0xFF141414), - background = Color.White, - onPrimary = Color(0xFF141414) -) - -@Composable -fun WalletConnectTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit -) { - val colorScheme = if (darkTheme) darkColorScheme else lightColorScheme - - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme - } - } - - MaterialTheme( - colors = colorScheme, - content = content - ) -} \ No newline at end of file diff --git a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/view/ViewActivity.kt b/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/view/ViewActivity.kt deleted file mode 100644 index 2882a5dee..000000000 --- a/sample/modal/src/main/kotlin/com/walletconnect/sample/modal/view/ViewActivity.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.walletconnect.sample.modal.view - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import com.walletconnect.sample.modal.R -import com.walletconnect.web3.modal.ui.Web3ModalView - -class ViewActivity: AppCompatActivity(R.layout.activity_view) { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val view = findViewById(R.id.web3Modal) - - view.setOnCloseModal { finish() } - } - -} \ No newline at end of file diff --git a/sample/modal/src/main/res/layout/activity_view.xml b/sample/modal/src/main/res/layout/activity_view.xml index f01a4f678..79aadb46a 100644 --- a/sample/modal/src/main/res/layout/activity_view.xml +++ b/sample/modal/src/main/res/layout/activity_view.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - diff --git a/sample/modal/src/main/res/navigation/nav_graph.xml b/sample/modal/src/main/res/navigation/nav_graph.xml index f92760220..51acc3245 100644 --- a/sample/modal/src/main/res/navigation/nav_graph.xml +++ b/sample/modal/src/main/res/navigation/nav_graph.xml @@ -7,7 +7,7 @@ + android:name="com.reown.sample.modal.navComponent.HomeFragment"> diff --git a/sample/modal/src/main/res/values/strings.xml b/sample/modal/src/main/res/values/strings.xml index 469ad9b7b..36c161df5 100644 --- a/sample/modal/src/main/res/values/strings.xml +++ b/sample/modal/src/main/res/values/strings.xml @@ -1,4 +1,4 @@ - Web3Modal Lab + AppKit Lab Connect Wallet \ No newline at end of file diff --git a/sample/wallet/build.gradle.kts b/sample/wallet/build.gradle.kts index 13de64347..8ebdab27e 100644 --- a/sample/wallet/build.gradle.kts +++ b/sample/wallet/build.gradle.kts @@ -8,12 +8,12 @@ plugins { } android { - namespace = "com.walletconnect.sample.wallet" + namespace = "com.reown.sample.wallet" compileSdk = COMPILE_SDK // hash of all sdk versions from Versions.kt defaultConfig { - applicationId = "com.walletconnect.sample.wallet" + applicationId = "com.reown.sample.wallet" minSdk = MIN_SDK targetSdk = TARGET_SDK versionName = SAMPLE_VERSION_NAME @@ -28,18 +28,18 @@ android { buildTypes { getByName("release") { manifestPlaceholders["pathPrefix"] = "/wallet_release" - buildConfigField("String", "WALLET_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_release\"") + buildConfigField("String", "WALLET_APP_LINK", "\"https://dev.lab.web3modal.com/wallet_release\"") } getByName("internal") { manifestPlaceholders["pathPrefix"] = "/wallet_internal" - buildConfigField("String", "WALLET_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_internal\"") + buildConfigField("String", "WALLET_APP_LINK", "\"https://dev.lab.web3modal.com/wallet_internal\"") } getByName("debug") { manifestPlaceholders["pathPrefix"] = "/wallet_debug" - buildConfigField("String", "WALLET_APP_LINK", "\"https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app/wallet_debug\"") + buildConfigField("String", "WALLET_APP_LINK", "\"https://dev.lab.web3modal.com/wallet_debug\"") } } @@ -123,15 +123,15 @@ dependencies { // WalletConnect debugImplementation(project(":core:android")) - debugImplementation(project(":product:web3wallet")) + debugImplementation(project(":product:walletkit")) debugImplementation(project(":protocol:notify")) internalImplementation(project(":core:android")) - internalImplementation(project(":product:web3wallet")) + internalImplementation(project(":product:walletkit")) internalImplementation(project(":protocol:notify")) - releaseImplementation(platform("com.walletconnect:android-bom:$BOM_VERSION")) - releaseImplementation("com.walletconnect:android-core") - releaseImplementation("com.walletconnect:web3wallet") - releaseImplementation("com.walletconnect:notify") + releaseImplementation(platform("com.reown:android-bom:$BOM_VERSION")) + releaseImplementation("com.reown:android-core") + releaseImplementation("com.reown:walletkit") + releaseImplementation("com.reown:notify") } diff --git a/sample/wallet/proguard-rules.pro b/sample/wallet/proguard-rules.pro index 4ba904ad8..25ef9e66e 100644 --- a/sample/wallet/proguard-rules.pro +++ b/sample/wallet/proguard-rules.pro @@ -1,7 +1,7 @@ -keepnames class com.fasterxml.jackson.** { *; } -dontwarn com.fasterxml.jackson.databind.** --keep class com.walletconnect.web3.wallet.client.Wallet$Model$Cacao$Signature { *; } --keep class com.walletconnect.web3.wallet.client.Wallet$Model$Cacao { *; } --keep class com.walletconnect.web3.wallet.client.Wallet$Model { *; } --keep class com.walletconnect.web3.wallet.client.Wallet { *; } \ No newline at end of file +-keep class com.reown.walletkit.client.Wallet$Model$Cacao$Signature { *; } +-keep class com.reown.walletkit.client.Wallet$Model$Cacao { *; } +-keep class com.reown.walletkit.client.Wallet$Model { *; } +-keep class com.reown.walletkit.client.Wallet { *; } \ No newline at end of file diff --git a/sample/wallet/src/main/AndroidManifest.xml b/sample/wallet/src/main/AndroidManifest.xml index f2d475781..79fca02d9 100644 --- a/sample/wallet/src/main/AndroidManifest.xml +++ b/sample/wallet/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ @@ -35,36 +35,47 @@ - + + + android:host="wc" + android:scheme="kotlin-web3wallet" /> + + - + + + + + + + + + diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitApplication.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitApplication.kt new file mode 100644 index 000000000..32ed5ed8a --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitApplication.kt @@ -0,0 +1,155 @@ +package com.reown.sample.wallet + +import android.app.Application +import com.google.firebase.appdistribution.FirebaseAppDistribution +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.mixpanel.android.mpmetrics.MixpanelAPI +import com.pandulapeter.beagle.Beagle +import com.pandulapeter.beagle.common.configuration.Behavior +import com.pandulapeter.beagle.log.BeagleLogger +import com.pandulapeter.beagle.logOkHttp.BeagleOkHttpLogger +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.wcKoinApp +import com.reown.foundation.util.Logger +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.NotificationHandler +import com.reown.sample.wallet.domain.NotifyDelegate +import com.reown.sample.wallet.domain.mixPanel +import com.reown.sample.wallet.ui.state.ConnectionState +import com.reown.sample.wallet.ui.state.connectionStateFlow +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import org.koin.core.qualifier.named +import timber.log.Timber +import com.reown.sample.common.BuildConfig as CommonBuildConfig + +class WalletKitApplication : Application() { + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + override fun onCreate() { + super.onCreate() + EthAccountDelegate.application = this + + val projectId = BuildConfig.PROJECT_ID + val appMetaData = Core.Model.AppMetaData( + name = "Kotlin Wallet", + description = "Kotlin Wallet Implementation", + url = "https://dev.lab.web3modal.com", + icons = listOf("https://raw.githubusercontent.com/WalletConnect/walletconnect-assets/master/Icon/Gradient/Icon.png"), + redirect = "kotlin-web3wallet://request", + appLink = BuildConfig.WALLET_APP_LINK, + linkMode = true + ) + + CoreClient.initialize( + application = this, + projectId = projectId, + metaData = appMetaData, + onError = { error -> + Firebase.crashlytics.recordException(error.throwable) + println("Init error: ${error.throwable.stackTraceToString()}") + scope.launch { + connectionStateFlow.emit(ConnectionState.Error(error.throwable.message ?: "")) + } + } + ) + + println("Account: ${EthAccountDelegate.account}") + + WalletKit.initialize(Wallet.Params.Init(core = CoreClient), + onSuccess = { println("Web3Wallet initialized") }, + onError = { error -> + Firebase.crashlytics.recordException(error.throwable) + println(error.throwable.stackTraceToString()) + }) + + FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() + + NotifyClient.initialize( + init = Notify.Params.Init(CoreClient) + ) { error -> + Firebase.crashlytics.recordException(error.throwable) + println(error.throwable.stackTraceToString()) + } + + mixPanel = MixpanelAPI.getInstance(this, CommonBuildConfig.MIX_PANEL, true).apply { + identify(CoreClient.Push.clientId) + people.set("\$name", EthAccountDelegate.ethAddress) + } + + initializeBeagle() + + scope.launch { + supervisorScope { + wcKoinApp.koin.get().plant(object : Timber.Tree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (t != null) { + mixPanel.track("error: $t, message: $message") + } else { + mixPanel.track(message) + } + } + }) + + handleNotifyMessages() + } + } + } + + private fun initializeBeagle() { + Timber.plant( + object : Timber.Tree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + Beagle.log("${tag?.let { "[$it] " } ?: ""}$message", "Timber", t?.stackTraceToString(), timestamp = System.currentTimeMillis()) + } + } + ) + + Beagle.initialize( + application = this, + behavior = Behavior( + logBehavior = Behavior.LogBehavior(loggers = listOf(BeagleLogger)), + networkLogBehavior = Behavior.NetworkLogBehavior(networkLoggers = listOf(BeagleOkHttpLogger)) + ) + ) + } + + + private fun handleNotifyMessages() { + val scope = CoroutineScope(Dispatchers.Default) + + val notifyEventsJob = NotifyDelegate.notifyEvents + .filterIsInstance() + .onEach { notification -> NotificationHandler.addNotification(notification.notification) } + .launchIn(scope) + + + val notificationDisplayingJob = NotificationHandler.startNotificationDisplayingJob(scope, this) + + + notifyEventsJob.invokeOnCompletion { cause -> + onScopeCancelled(cause, "notifyEventsJob") + } + + notificationDisplayingJob.invokeOnCompletion { cause -> + onScopeCancelled(cause, "notificationDisplayingJob") + } + } + + private fun onScopeCancelled(error: Throwable?, job: String) { + wcKoinApp.koin.get(named(AndroidCommonDITags.LOGGER)).error("onScopeCancelled($job): $error") + } +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitMessageService.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitMessageService.kt new file mode 100644 index 000000000..051ec354b --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/WalletKitMessageService.kt @@ -0,0 +1,37 @@ +package com.reown.sample.wallet + +import android.annotation.SuppressLint +import com.google.firebase.messaging.RemoteMessage +import com.reown.android.Core +import com.reown.android.internal.common.di.AndroidCommonDITags +import com.reown.android.internal.common.wcKoinApp +import com.reown.android.push.notifications.PushMessagingService +import com.reown.foundation.util.Logger +import com.reown.sample.wallet.domain.NotificationHandler +import kotlinx.coroutines.runBlocking +import org.koin.core.qualifier.named + +@SuppressLint("MissingFirebaseInstanceTokenRefresh") +class WalletKitMessageService : PushMessagingService() { + private val logger: Logger by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.LOGGER)) } + + override fun onMessage(message: Core.Model.Message, originalMessage: RemoteMessage) { + runBlocking { NotificationHandler.addNotification(message) } + } + + override fun newToken(token: String) { + logger.log("Registering New Token Success:\t$token") + } + + override fun registeringFailed(token: String, throwable: Throwable) { + logger.log("Registering New Token Failed:\t$token") + } + + override fun onDefaultBehavior(message: RemoteMessage) { + logger.log("onDefaultBehavior: ${message.to}") + } + + override fun onError(throwable: Throwable, defaultMessage: RemoteMessage) { + logger.error("onError Message To: ${defaultMessage.to}, throwable: $throwable") + } +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegate.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/EthAccountDelegate.kt similarity index 98% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegate.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/EthAccountDelegate.kt index 6f313f61e..eff070699 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegate.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/EthAccountDelegate.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.domain +package com.reown.sample.wallet.domain import android.app.Application import android.content.Context diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/MixPanelDelegate.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/MixPanelDelegate.kt new file mode 100644 index 000000000..49c006382 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/MixPanelDelegate.kt @@ -0,0 +1,5 @@ +package com.reown.sample.wallet.domain + +import com.mixpanel.android.mpmetrics.MixpanelAPI + +lateinit var mixPanel: MixpanelAPI \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotificationHandler.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotificationHandler.kt similarity index 91% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotificationHandler.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotificationHandler.kt index 3b9f441ec..8d76bed62 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotificationHandler.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotificationHandler.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) -package com.walletconnect.sample.wallet.domain +package com.reown.sample.wallet.domain import android.app.NotificationChannel import android.app.NotificationManager @@ -16,11 +16,11 @@ import androidx.core.app.NotificationCompat import androidx.core.graphics.drawable.toBitmap import coil.ImageLoader import coil.request.ImageRequest -import com.walletconnect.android.Core -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.Web3WalletActivity +import com.reown.android.Core +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.WalletKitActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -89,16 +89,6 @@ object NotificationHandler { val url: String?, val iconUrl: String?, ) : Notification - - data class AuthRequest( - override val messageId: Int, - override val channelId: String, - override val title: String, - override val body: String, - val topic: String, - val url: String?, - val iconUrl: String?, - ) : Notification } private data class NotificationsWithMetadata(val notifications: List, val channelName: String, val iconUrl: String?) @@ -181,7 +171,7 @@ object NotificationHandler { } private fun buildPendingIntent(context: Context): PendingIntent { - val intent = Intent(context, Web3WalletActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } + val intent = Intent(context, WalletKitActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) } @@ -233,16 +223,6 @@ object NotificationHandler { val notification = when (message) { is Core.Model.Message.Simple -> Notification.Simple(message.hashCode(), W3W_CHANNEL_ID, message.title, message.body) is Core.Model.Message.Notify -> Notification.Decrypted(message.hashCode(), message.type, message.title, message.body, message.topic, message.url) - is Core.Model.Message.AuthRequest -> Notification.AuthRequest( - message.hashCode(), - W3W_CHANNEL_ID, - "New Authentication Request!", - "A new authentication request arrived from ${message.metadata.name}, please check your wallet", - message.pairingTopic, - message.metadata.url, - message.metadata.icons.firstOrNull() - ) - is Core.Model.Message.SessionRequest -> Notification.SessionRequest( message.hashCode(), W3W_CHANNEL_ID, @@ -253,7 +233,6 @@ object NotificationHandler { message.peerMetaData?.url, message.peerMetaData?.icons?.firstOrNull() ) - is Core.Model.Message.SessionProposal -> Notification.SessionProposal( message.hashCode(), @@ -266,7 +245,6 @@ object NotificationHandler { message.icons.firstOrNull(), message.redirect ) - is Core.Model.Message.SessionAuthenticate -> Notification.SessionAuthenticate( message.hashCode(), W3W_CHANNEL_ID, @@ -302,5 +280,4 @@ object NotificationHandler { .buildAndShowNotification(context, 5000) .launchIn(scope) } - } \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotifyDelegate.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotifyDelegate.kt similarity index 90% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotifyDelegate.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotifyDelegate.kt index 517c6a31c..ae565d0e6 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/NotifyDelegate.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/NotifyDelegate.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sample.wallet.domain +package com.reown.sample.wallet.domain -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/TestAccounts.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/TestAccounts.kt similarity index 88% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/TestAccounts.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/TestAccounts.kt index 566adaf4d..fbe9691fe 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/TestAccounts.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/TestAccounts.kt @@ -1,7 +1,7 @@ -package com.walletconnect.sample.wallet.domain +package com.reown.sample.wallet.domain -import com.walletconnect.sample.common.Chains -import com.walletconnect.util.hexToBytes +import com.reown.sample.common.Chains +import com.reown.util.hexToBytes import io.ipfs.multibase.Base16 val ACCOUNTS_1_EIP155_ADDRESS by lazy { EthAccountDelegate.account } diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/WCDelegate.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/WCDelegate.kt similarity index 85% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/WCDelegate.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/WCDelegate.kt index 2b6d3b75d..64e7b500e 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/WCDelegate.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/WCDelegate.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sample.wallet.domain +package com.reown.sample.wallet.domain import android.util.Log -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet +import com.reown.android.Core +import com.reown.android.CoreClient +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import org.json.JSONObject -object WCDelegate : Web3Wallet.WalletDelegate, CoreClient.CoreDelegate { +object WCDelegate : WalletKit.WalletDelegate, CoreClient.CoreDelegate { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val _coreEvents: MutableSharedFlow = MutableSharedFlow() val coreEvents: SharedFlow = _coreEvents.asSharedFlow() @@ -23,7 +23,6 @@ object WCDelegate : Web3Wallet.WalletDelegate, CoreClient.CoreDelegate { val walletEvents: SharedFlow = _walletEvents.asSharedFlow() private val _connectionState: MutableSharedFlow = MutableSharedFlow(replay = 1) val connectionState: SharedFlow = _connectionState.asSharedFlow() - var authRequestEvent: Pair? = null var sessionProposalEvent: Pair? = null var sessionAuthenticateEvent: Pair? = null var sessionRequestEvent: Pair? = null @@ -31,19 +30,10 @@ object WCDelegate : Web3Wallet.WalletDelegate, CoreClient.CoreDelegate { init { CoreClient.setDelegate(this) - Web3Wallet.setWalletDelegate(this) - } - - override fun onAuthRequest(authRequest: Wallet.Model.AuthRequest, verifyContext: Wallet.Model.VerifyContext) { - authRequestEvent = Pair(authRequest, verifyContext) - - scope.launch { - _walletEvents.emit(authRequest) - } + WalletKit.setWalletDelegate(this) } override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { - state.isAvailable scope.launch { _connectionState.emit(state) } diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/model/NotificationUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/model/NotificationUI.kt similarity index 81% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/model/NotificationUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/model/NotificationUI.kt index 0f8800af1..fc7f254b9 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/model/NotificationUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/domain/model/NotificationUI.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.domain.model +package com.reown.sample.wallet.domain.model data class NotificationUI( val id: String, diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/NotifyEvent.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/NotifyEvent.kt similarity index 76% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/NotifyEvent.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/NotifyEvent.kt index d6dc60c52..416b51085 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/NotifyEvent.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/NotifyEvent.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui +package com.reown.sample.wallet.ui sealed interface NotifyEvent diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/WalletKitActivity.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/WalletKitActivity.kt new file mode 100644 index 000000000..a6f8a856d --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/WalletKitActivity.kt @@ -0,0 +1,312 @@ +@file:OptIn( + ExperimentalMaterialApi::class, + ExperimentalMaterialNavigationApi::class, + ExperimentalAnimationApi::class +) + +package com.reown.sample.wallet.ui + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.rememberModalBottomSheetState +import androidx.core.content.ContextCompat +import androidx.core.net.toUri +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import androidx.navigation.NavHostController +import com.google.accompanist.navigation.animation.rememberAnimatedNavController +import com.google.accompanist.navigation.material.BottomSheetNavigator +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.pandulapeter.beagle.Beagle +import com.pandulapeter.beagle.modules.DividerModule +import com.pandulapeter.beagle.modules.HeaderModule +import com.pandulapeter.beagle.modules.LogListModule +import com.pandulapeter.beagle.modules.NetworkLogListModule +import com.pandulapeter.beagle.modules.PaddingModule +import com.pandulapeter.beagle.modules.ScreenCaptureToolboxModule +import com.pandulapeter.beagle.modules.TextInputModule +import com.pandulapeter.beagle.modules.TextModule +import com.reown.android.CoreClient +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.utils.cacao.sign +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.notify.client.cacao.CacaoSigner +import com.reown.sample.common.ui.theme.WCSampleAppTheme +import com.reown.sample.wallet.BuildConfig +import com.reown.sample.wallet.R +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.NotifyDelegate +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel +import com.reown.sample.wallet.ui.routes.host.WalletSampleHost +import com.reown.util.hexToBytes +import com.reown.walletkit.client.WalletKit +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import timber.log.Timber +import java.net.URLEncoder + +class WalletKitActivity : AppCompatActivity() { + private lateinit var navController: NavHostController + private val web3walletViewModel = Web3WalletViewModel() + private val connectionsViewModel = ConnectionsViewModel() + + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + // FCM SDK (and your app) can post notifications. + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent(web3walletViewModel, connectionsViewModel) + handleWeb3WalletEvents(web3walletViewModel, connectionsViewModel) + askNotificationPermission() + handleErrors() + handleAppLink(intent) + registerAccount() + setBeagle() + } + + private fun setContent( + web3walletViewModel: Web3WalletViewModel, + connectionsViewModel: ConnectionsViewModel, + ) { + setContent { + val sheetState = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Hidden, + skipHalfExpanded = true + ) + val bottomSheetNavigator = BottomSheetNavigator(sheetState) + val navController = rememberAnimatedNavController(bottomSheetNavigator) + this.navController = navController + val sharedPref = getPreferences(MODE_PRIVATE) + val getStartedVisited = sharedPref.getBoolean("get_started_visited", false) + WCSampleAppTheme { + WalletSampleHost( + bottomSheetNavigator, + navController, + web3walletViewModel, + connectionsViewModel, + getStartedVisited + ) + } + } + } + + private fun handleErrors() { + NotifyDelegate.notifyErrors + .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) + .onEach { error -> Timber.e(error.throwable) } + .launchIn(lifecycleScope) + } + + private fun handleWeb3WalletEvents( + web3walletViewModel: Web3WalletViewModel, + connectionsViewModel: ConnectionsViewModel, + ) { + web3walletViewModel.sessionRequestStateFlow + .onEach { + if (it.arrayOfArgs.isNotEmpty()) { + web3walletViewModel.showRequestLoader(false) + navigateWhenReady { + navController.navigate(Route.SessionRequest.path) + } + } + } + .launchIn(lifecycleScope) + + web3walletViewModel.walletEvents + .onEach { event -> + when (event) { + is SignEvent.SessionProposal -> navigateWhenReady { navController.navigate(Route.SessionProposal.path) } + is SignEvent.SessionAuthenticate -> navigateWhenReady { navController.navigate(Route.SessionAuthenticate.path) } + is SignEvent.ExpiredRequest -> { + if (navController.currentDestination?.route != Route.Connections.path) { + navController.popBackStack(route = Route.Connections.path, inclusive = false) + } + Toast.makeText(baseContext, "Request expired", Toast.LENGTH_SHORT).show() + } + + is SignEvent.Disconnect -> { + connectionsViewModel.refreshConnections() + + if (navController.currentDestination?.route != Route.Connections.path && + navController.currentDestination?.route != Route.SessionProposal.path && + navController.currentDestination?.route != Route.SessionAuthenticate.path + ) { + navController.navigate(Route.Connections.path) + } + } + + else -> Unit + } + } + .launchIn(lifecycleScope) + } + + private suspend fun navigateWhenReady(navigate: () -> Unit) { + if (!::navController.isInitialized) { + delay(400) + navigate() + } else { + navigate() + } + } + + private fun handleAppLink(intent: Intent?) { + if (intent?.dataString?.contains("wc_ev") == true) { + WalletKit.dispatchEnvelope(intent.dataString ?: "") { + lifecycleScope.launch(Dispatchers.Main) { + Toast.makeText(this@WalletKitActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() + } + } + } else { + when { + intent?.dataString?.startsWith("kotlin-web3wallet:/wc") == true -> { + val uri = intent.dataString?.replace("kotlin-web3wallet:/wc", "kotlin-web3wallet://wc") + intent.setData(uri?.toUri()) + } + + intent?.dataString?.startsWith("wc:") == true && intent.dataString?.contains("requestId") == false -> { + val uri = "kotlin-web3wallet://wc?uri=" + URLEncoder.encode(intent.dataString, "UTF-8") + intent.setData(uri.toUri()) + } + } + + if (intent?.dataString?.startsWith("kotlin-web3wallet://request") == true || intent?.dataString?.contains("requestId") == true) { + lifecycleScope.launch { + navigateWhenReady { + if (navController.currentDestination?.route != Route.SessionRequest.path) { + web3walletViewModel.showRequestLoader(true) + } + } + } + } + + if (intent?.dataString?.startsWith("kotlin-web3wallet://request") == false && intent.dataString?.contains("requestId") == false + ) { + lifecycleScope.launch { + navigateWhenReady { + web3walletViewModel.pair(intent.dataString.toString()) + } + } + } + } + } + + private fun setBeagle() { + Beagle.set( + HeaderModule( + title = getString(R.string.app_name), + subtitle = BuildConfig.APPLICATION_ID, + text = "${BuildConfig.BUILD_TYPE} v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" + ), + ScreenCaptureToolboxModule(), + DividerModule(), + TextModule("Logs", TextModule.Type.SECTION_HEADER), + NetworkLogListModule(), + LogListModule(), + DividerModule(), + TextModule(text = EthAccountDelegate.ethAddress) { + (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("Account", EthAccountDelegate.ethAddress)) + }, + PaddingModule(size = PaddingModule.Size.LARGE), + TextModule(text = EthAccountDelegate.privateKey) { + (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("Private Key", EthAccountDelegate.privateKey)) + }, + PaddingModule(size = PaddingModule.Size.LARGE), + TextModule(text = CoreClient.Push.clientId, id = CoreClient.Push.clientId) { + (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("ClientId", CoreClient.Push.clientId)) + }, + DividerModule(), + TextInputModule( + text = "Import Private Key", + areRealTimeUpdatesEnabled = false, + validator = { text -> + !text.startsWith("0x") && text.length == 64 + }, + onValueChanged = { text -> + NotifyClient.unregister( + params = Notify.Params.Unregister( + EthAccountDelegate.ethAddress, + ), + onSuccess = { + println("Unregister Success") + EthAccountDelegate.privateKey = text + registerAccount() + }, + onError = { println(it.throwable.stackTraceToString()) } + ) + } + ) + ) + } + + private fun registerAccount() { + val account = EthAccountDelegate.ethAddress + val domain = BuildConfig.APPLICATION_ID + val isRegistered = NotifyClient.isRegistered(params = Notify.Params.IsRegistered(account = account, domain = domain)) + + if (!isRegistered) { + NotifyClient.prepareRegistration( + params = Notify.Params.PrepareRegistration(account = account, domain = domain), + onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> + println("PrepareRegistration Success: $cacaoPayloadWithIdentityPrivateKey") + + val signature = CacaoSigner.sign(message, EthAccountDelegate.privateKey.hexToBytes(), SignatureType.EIP191) + + NotifyClient.register( + params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), + onSuccess = { println("Register Success") }, + onError = { println(it.throwable.stackTraceToString()) } + ) + + }, + onError = { println(it.throwable.stackTraceToString()) } + ) + } else { + println("$account is already registered") + } + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + + handleAppLink(intent) + } + + private fun askNotificationPermission() { + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission( + this, + android.Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + ) { + // FCM SDK (and your app) can post notifications. + } else { + // Directly ask for the permission + requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS) + } + } + } +} diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletEvent.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletEvent.kt new file mode 100644 index 000000000..d0ed7c5fd --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletEvent.kt @@ -0,0 +1,14 @@ +package com.reown.sample.wallet.ui + +sealed interface Web3WalletEvent + +object NoAction : Web3WalletEvent, NotifyEvent + +interface SignEvent : Web3WalletEvent { + object SessionProposal : SignEvent + object SessionAuthenticate : SignEvent + data class SessionRequest(val arrayOfArgs: ArrayList, val numOfArgs: Int) : SignEvent + object ExpiredRequest : SignEvent + object Disconnect : SignEvent + data class ConnectionState(val isAvailable: Boolean) : SignEvent +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletNavGraph.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletNavGraph.kt new file mode 100644 index 000000000..6e6bf3b75 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletNavGraph.kt @@ -0,0 +1,179 @@ +@file:OptIn(ExperimentalAnimationApi::class) + +package com.reown.sample.wallet.ui + +import android.annotation.SuppressLint +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.core.tween +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ModalBottomSheetDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.DialogProperties +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavDeepLink +import androidx.navigation.NavHostController +import androidx.navigation.NavType +import androidx.navigation.compose.dialog +import androidx.navigation.navArgument +import com.google.accompanist.navigation.animation.AnimatedNavHost +import com.google.accompanist.navigation.animation.composable +import com.google.accompanist.navigation.material.BottomSheetNavigator +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.google.accompanist.navigation.material.ModalBottomSheetLayout +import com.google.accompanist.navigation.material.bottomSheet +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.bottomsheet_routes.scan_uri.ScanUriRoute +import com.reown.sample.wallet.ui.routes.bottomsheet_routes.update_subscription.UpdateSubscriptionRoute +import com.reown.sample.wallet.ui.routes.composable_routes.connection_details.ConnectionDetailsRoute +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsRoute +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel +import com.reown.sample.wallet.ui.routes.composable_routes.get_started.GetStartedRoute +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.InboxRoute +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.InboxViewModel +import com.reown.sample.wallet.ui.routes.composable_routes.notifications.NotificationsScreenRoute +import com.reown.sample.wallet.ui.routes.composable_routes.settings.SettingsRoute +import com.reown.sample.wallet.ui.routes.dialog_routes.paste_uri.PasteUriRoute +import com.reown.sample.wallet.ui.routes.dialog_routes.session_authenticate.SessionAuthenticateRoute +import com.reown.sample.wallet.ui.routes.dialog_routes.session_proposal.SessionProposalRoute +import com.reown.sample.wallet.ui.routes.dialog_routes.session_request.SessionRequestRoute +import com.reown.sample.wallet.ui.routes.dialog_routes.snackbar_message.SnackbarMessageRoute + +@SuppressLint("RestrictedApi") +@ExperimentalMaterialNavigationApi +@Composable +fun Web3WalletNavGraph( + bottomSheetNavigator: BottomSheetNavigator, + navController: NavHostController, + web3walletViewModel: Web3WalletViewModel, + connectionsViewModel: ConnectionsViewModel, + getStartedVisited: Boolean, + modifier: Modifier = Modifier, + startDestination: String = if (getStartedVisited) Route.Connections.path else Route.GetStarted.path, +) { + var scrimColor by remember { mutableStateOf(Color.Unspecified) } + val inboxViewModel: InboxViewModel = viewModel() + + navController.addOnDestinationChangedListener( + listener = { _, destination, _ -> + if (destination.route == Route.Connections.path) { + WCDelegate.currentId = null + } + }) + + ModalBottomSheetLayout( + modifier = modifier, + bottomSheetNavigator = bottomSheetNavigator, + sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), + sheetBackgroundColor = Color.Transparent, sheetElevation = 0.dp, + scrimColor = scrimColor + ) { + val sheetState = remember { bottomSheetNavigator.navigatorSheetState } + + AnimatedNavHost( + navController = navController, + startDestination = startDestination, + enterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, + animationSpec = tween(700) + ) + }, + exitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, + animationSpec = tween(700) + ) + }, + popEnterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, + animationSpec = tween(700) + ) + }, + popExitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, + animationSpec = tween(700) + ) + } + ) { + composable(Route.GetStarted.path) { + GetStartedRoute(navController) + } + composable(Route.Connections.path) { + ConnectionsRoute(navController, connectionsViewModel, web3walletViewModel) + } + composable("${Route.ConnectionDetails.path}/{connectionId}", arguments = listOf( + navArgument("connectionId") { type = NavType.IntType } + )) { + ConnectionDetailsRoute(navController, it.arguments?.getInt("connectionId"), connectionsViewModel) + } + composable( + "${Route.Notifications.path}/{topic}", arguments = listOf( + navArgument("topic") { + type = NavType.Companion.StringType + nullable = false + }, + ) + ) { + NotificationsScreenRoute(navController, it.arguments?.getString("topic")!!, inboxViewModel) + } + composable(Route.Inbox.path) { + InboxRoute(navController, inboxViewModel) + } + composable(Route.Settings.path) { + SettingsRoute(navController) + } + bottomSheet(Route.ScanUri.path) { + web3walletViewModel.showLoader(false) + scrimColor = Color.Unspecified + ScanUriRoute(navController, sheetState, onScanSuccess = { + web3walletViewModel.pair(it) + }) + } + bottomSheet( + "${Route.UpdateSubscription.path}/{topic}", arguments = listOf( + navArgument("topic") { + type = NavType.Companion.StringType + nullable = false + }) + ) { + scrimColor = ModalBottomSheetDefaults.scrimColor + UpdateSubscriptionRoute(navController, sheetState, it.arguments?.getString("topic")!!) + } + dialog(Route.SessionProposal.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { + SessionProposalRoute(navController) + } + dialog(Route.SessionAuthenticate.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { + SessionAuthenticateRoute(navController, connectionsViewModel) + } + dialog( + Route.SessionRequest.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false) + ) { + SessionRequestRoute(navController) + } + dialog(Route.PasteUri.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { + PasteUriRoute(onSubmit = { + web3walletViewModel.pair(it) + navController.popBackStack() + }) + } + bottomSheet("${Route.SnackbarMessage.path}/{message}", arguments = listOf( + navArgument("message") { type = NavType.StringType } + )) { + scrimColor = Color.Unspecified + SnackbarMessageRoute(navController, it.arguments?.getString("message")) + } + } + } +} + diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletViewModel.kt similarity index 76% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletViewModel.kt index 5e8b4ccb6..372130102 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/Web3WalletViewModel.kt @@ -1,17 +1,19 @@ -package com.walletconnect.sample.wallet.ui +package com.reown.sample.wallet.ui import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import com.walletconnect.android.Core -import com.walletconnect.sample.wallet.domain.ISSUER -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.state.ConnectionState -import com.walletconnect.sample.wallet.ui.state.PairingEvent -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet +import com.reown.android.Core +import com.reown.android.internal.common.exception.InvalidProjectIdException +import com.reown.android.internal.common.exception.ProjectIdDoesNotExistException +import com.reown.sample.wallet.domain.ISSUER +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.sample.wallet.ui.state.ConnectionState +import com.reown.sample.wallet.ui.state.PairingEvent +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -68,7 +70,17 @@ class Web3WalletViewModel : ViewModel() { val connectionState = if (it.isAvailable) { ConnectionState.Ok } else { - ConnectionState.Error("No Internet connection, please check your internet connection and try again") + val message = when (it.reason) { + is Wallet.Model.ConnectionState.Reason.ConnectionFailed -> { + if ((it.reason as Wallet.Model.ConnectionState.Reason.ConnectionFailed).throwable is ProjectIdDoesNotExistException || + (it.reason as Wallet.Model.ConnectionState.Reason.ConnectionFailed).throwable is InvalidProjectIdException + ) "Invalid Project Id" else "Connection failed" + } + + else -> "Connection closed" + } + + ConnectionState.Error(message) } connectivityStateFlow.value = connectionState }.launchIn(viewModelScope) @@ -83,7 +95,6 @@ class Web3WalletViewModel : ViewModel() { viewModelScope.launch { _eventsSharedFlow.emit(PairingEvent.ProposalExpired("Proposal expired, please pair again")) } - } is Wallet.Model.ExpiredRequest -> SignEvent.ExpiredRequest @@ -103,13 +114,6 @@ class Web3WalletViewModel : ViewModel() { } } - is Wallet.Model.AuthRequest -> { - _isLoadingFlow.value = false - val message = Web3Wallet.formatMessage(Wallet.Params.FormatMessage(wcEvent.payloadParams, ISSUER)) - ?: throw Exception("Error formatting message") - AuthEvent.OnRequest(wcEvent.id, message) - } - is Wallet.Model.SessionAuthenticate -> { _isLoadingFlow.value = false SignEvent.SessionAuthenticate @@ -120,6 +124,16 @@ class Web3WalletViewModel : ViewModel() { _isLoadingFlow.value = false SignEvent.SessionProposal } + is Wallet.Model.Error -> { + if (wcEvent.throwable.message?.contains("No proposal or pending session authenticate request for pairing topic") == true){ + viewModelScope.launch { + _isLoadingFlow.value = false + _eventsSharedFlow.emit(PairingEvent.ProposalExpired("No proposal or pending session authenticate request for pairing topic: Proposal already consumed")) + } + } else { + println(wcEvent.throwable) + } + } else -> NoAction } @@ -140,7 +154,7 @@ class Web3WalletViewModel : ViewModel() { try { val pairingParams = Wallet.Params.Pair(pairingUri.removePrefix("kotlin-web3wallet://wc?uri=")) - Web3Wallet.pair(pairingParams) { error -> + WalletKit.pair(pairingParams) { error -> Firebase.crashlytics.recordException(error.throwable) viewModelScope.launch { _isLoadingFlow.value = false diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Buttons.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Buttons.kt new file mode 100644 index 000000000..92cea5f57 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Buttons.kt @@ -0,0 +1,80 @@ +package com.reown.sample.wallet.ui.common + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.reown.sample.wallet.ui.common.generated.ButtonWithLoader + + +@Composable +fun Buttons( + allowButtonColor: Color, + modifier: Modifier = Modifier, + onCancel: () -> Unit = {}, + onConfirm: () -> Unit = {}, + isLoadingConfirm: Boolean, + isLoadingCancel: Boolean +) { + Row(modifier = modifier) { + Spacer(modifier = Modifier.width(18.dp)) + ButtonWithLoader( + buttonColor = Color(0xFFD6D6D6), + loaderColor = Color(0xFF000000), + modifier = Modifier + .weight(1f) + .height(46.dp) + .clickable { onCancel() }, + isLoading = isLoadingCancel, + content = { + Text( + text = "Cancel", + style = TextStyle( + fontSize = 20.0.sp, + fontWeight = FontWeight.SemiBold, + color = Color(0xFF000000), + ), + modifier = modifier.wrapContentHeight(align = Alignment.CenterVertically) + ) + } + ) + Spacer(modifier = Modifier.width(12.dp)) + ButtonWithLoader( + buttonColor = allowButtonColor, + loaderColor = Color(0xFFFFFFFF), + modifier = Modifier + .weight(1f) + .height(46.dp) + .clickable { onConfirm() }, + isLoadingConfirm, + content = { + Text( + text = "Confirm", + style = TextStyle( + fontSize = 20.0.sp, + fontWeight = FontWeight.SemiBold, + color = Color( + alpha = 255, + red = 255, + green = 255, + blue = 255 + ), + ), + modifier = modifier.wrapContentHeight(align = Alignment.CenterVertically) + ) + } + ) + Spacer(modifier = Modifier.width(20.dp)) + } +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Content.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Content.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Content.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Content.kt index ae41f1dfb..b59faed66 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Content.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Content.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common +package com.reown.sample.wallet.ui.common import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -15,7 +15,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/ImageUrl.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/ImageUrl.kt new file mode 100644 index 000000000..8fda9e151 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/ImageUrl.kt @@ -0,0 +1,15 @@ +package com.reown.sample.wallet.ui.common + +data class ImageUrl( + val small: String, + val medium: String, + val large: String, +) + +fun List.toImageUrl(): ImageUrl { + return ImageUrl( + small = this.getOrElse(0) { "" }, + medium = this.getOrElse(1) { "" }, + large = this.getOrElse(2) { "" }, + ) +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/InnerContent.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/InnerContent.kt similarity index 90% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/InnerContent.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/InnerContent.kt index d260094aa..e29795669 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/InnerContent.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/InnerContent.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common +package com.reown.sample.wallet.ui.common import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -11,7 +11,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Namespaces.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Namespaces.kt similarity index 89% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Namespaces.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Namespaces.kt index b5ca32d26..8ce342969 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Namespaces.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/Namespaces.kt @@ -1,6 +1,6 @@ -package com.walletconnect.sample.wallet.ui.common +package com.reown.sample.wallet.ui.common -import com.walletconnect.web3.wallet.client.Wallet +import com.reown.walletkit.client.Wallet fun getAllMethodsByChainId(namespace: Wallet.Model.Namespace.Proposal, chainId: String): List { return namespace.methods.takeIf { namespace.chains != null && namespace.chains!!.contains(chainId) } ?: emptyList() diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/SemiTransparentDialog.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/SemiTransparentDialog.kt similarity index 88% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/SemiTransparentDialog.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/SemiTransparentDialog.kt index 35b647c83..73a8ecd79 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/SemiTransparentDialog.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/SemiTransparentDialog.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common +package com.reown.sample.wallet.ui.common import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable fun SemiTransparentDialog(backgroundColor: Color = themedColor(Color(0xFF242425), Color(0xFFFFFFFF)), content: @Composable () -> Unit) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelRow.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelRow.kt similarity index 90% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelRow.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelRow.kt index f12b640b4..f6f5d3ecb 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelRow.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelRow.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.blue +package com.reown.sample.wallet.ui.common.blue import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelText.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelText.kt similarity index 91% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelText.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelText.kt index 0198e6ede..8bc4a2436 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelText.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelText.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.blue +package com.reown.sample.wallet.ui.common.blue import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding @@ -13,7 +13,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt similarity index 91% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt index 9b5ef54ae..18c6f1d3e 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/blue/BlueLabelTextsSection.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.blue +package com.reown.sample.wallet.ui.common.blue import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -11,8 +11,8 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.ui.common.InnerContent -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.wallet.ui.common.InnerContent +import com.reown.sample.common.ui.themedColor @Composable fun BlueLabelTexts(title: String, values: List, displayEndSpacer: Boolean = false) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/ButtonWithLoader.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/ButtonWithLoader.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/ButtonWithLoader.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/ButtonWithLoader.kt index f68af8ef1..ae0c5cc75 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/ButtonWithLoader.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/ButtonWithLoader.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.generated +package com.reown.sample.wallet.ui.common.generated import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CancelButton.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CancelButton.kt similarity index 96% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CancelButton.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CancelButton.kt index a7d872d52..02617bf2d 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CancelButton.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CancelButton.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.generated +package com.reown.sample.wallet.ui.common.generated import androidx.compose.foundation.background import androidx.compose.foundation.layout.* diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CloseButton.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CloseButton.kt similarity index 77% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CloseButton.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CloseButton.kt index 1a6c2afe6..a849d0ee5 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/CloseButton.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/CloseButton.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sample.wallet.ui.common.generated +package com.reown.sample.wallet.ui.common.generated import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import com.walletconnect.sample.wallet.R +import com.reown.sample.wallet.R @Composable fun CloseButton(modifier: Modifier = Modifier) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/GetStarted.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/GetStarted.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/GetStarted.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/GetStarted.kt index 245c3c310..152263ada 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/GetStarted.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/GetStarted.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.generated +package com.reown.sample.wallet.ui.common.generated import androidx.compose.foundation.layout.* import androidx.compose.material.Text diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/Welcome.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/Welcome.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/Welcome.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/Welcome.kt index 2b9cd8550..5bfebfc42 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/generated/Welcome.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/generated/Welcome.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.generated +package com.reown.sample.wallet.ui.common.generated import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -15,7 +15,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.common.ui.themedColor @Composable fun Welcome(modifier: Modifier = Modifier) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerSection.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerSection.kt similarity index 95% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerSection.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerSection.kt index b6c1022c6..38b731df0 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerSection.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerSection.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.peer +package com.reown.sample.wallet.ui.common.peer import android.net.Uri import androidx.compose.foundation.Image @@ -31,8 +31,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R @Composable @@ -51,7 +51,7 @@ fun Peer(peerUI: PeerUI, actionText: String?, peerContextUI: PeerContextUI? = nu .data(peerUI.peerIcon) .size(60) .crossfade(true) - .error(com.walletconnect.sample.common.R.drawable.ic_walletconnect_circle_blue) + .error(com.reown.sample.common.R.drawable.ic_walletconnect_circle_blue) .listener( onSuccess = { request, metadata -> println("onSuccess: $request, $metadata") }, onError = { _, throwable -> println("Error loading image: ${throwable.throwable.message}") }) diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerUI.kt similarity index 88% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerUI.kt index 3c008c4d6..372ea150b 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/peer/PeerUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/peer/PeerUI.kt @@ -1,11 +1,11 @@ -package com.walletconnect.sample.wallet.ui.common.peer +package com.reown.sample.wallet.ui.common.peer import androidx.compose.ui.graphics.Color -import com.walletconnect.sample.common.ui.theme.mismatch_color -import com.walletconnect.sample.common.ui.theme.unverified_color -import com.walletconnect.sample.common.ui.theme.verified_color -import com.walletconnect.sample.wallet.R -import com.walletconnect.web3.wallet.client.Wallet +import com.reown.sample.common.ui.theme.mismatch_color +import com.reown.sample.common.ui.theme.unverified_color +import com.reown.sample.common.ui.theme.verified_color +import com.reown.sample.wallet.R +import com.reown.walletkit.client.Wallet data class PeerUI( diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt similarity index 81% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt index 0d2019d60..74f83ae89 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/ActiveSubscriptionsUI.kt @@ -1,8 +1,8 @@ -package com.walletconnect.sample.wallet.ui.common.subscriptions +package com.reown.sample.wallet.ui.common.subscriptions -import com.walletconnect.notify.client.Notify -import com.walletconnect.sample.wallet.ui.common.ImageUrl -import com.walletconnect.sample.wallet.ui.common.toImageUrl +import com.reown.notify.client.Notify +import com.reown.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.toImageUrl data class ActiveSubscriptionsUI( val topic: String, diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/NotificationIcon.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/NotificationIcon.kt similarity index 93% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/NotificationIcon.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/NotificationIcon.kt index b7df02f7a..e631f8757 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/NotificationIcon.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/NotificationIcon.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.subscriptions +package com.reown.sample.wallet.ui.common.subscriptions import androidx.compose.foundation.background import androidx.compose.foundation.layout.size @@ -15,7 +15,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest -import com.walletconnect.sample.wallet.R +import com.reown.sample.wallet.R @Composable fun NotificationIcon(size: Dp, url: String?) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt similarity index 91% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt index 57493305b..e5f541336 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/common/subscriptions/SubscriptionIcon.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.common.subscriptions +package com.reown.sample.wallet.ui.common.subscriptions import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -17,8 +17,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.ImageUrl @Composable diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/Route.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/Route.kt new file mode 100644 index 000000000..591f9e0b6 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/Route.kt @@ -0,0 +1,29 @@ +package com.reown.sample.wallet.ui.routes + +import androidx.navigation.NavController + +sealed class Route(val path: String) { + object GetStarted : Route("get_started") + object Connections : Route("connections") + object SessionProposal : Route("session_proposal") + object SessionRequest : Route("session_request") + object SessionAuthenticate : Route("session_authenticate") + object PasteUri : Route("paste_uri") + object ScanUri : Route("scan_uri") + + object ConnectionDetails : Route("connection_details") + object SnackbarMessage : Route("snackbar_message") + object ExploreDapps : Route("explore_dapps") + object Inbox : Route("inbox") + object Notifications : Route("notifications") + object UpdateSubscription : Route("update_subscription") + object Settings : Route("settings") +} + +fun NavController.showSnackbar(message: String) { + navigate("${Route.SnackbarMessage.path}/$message") +} + + + + diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt similarity index 96% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt index 0ff9f1c86..d7745435c 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/QrCodeAnalyzer.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.scan_uri +package com.reown.sample.wallet.ui.routes.bottomsheet_routes.scan_uri import androidx.camera.core.ExperimentalGetImage import androidx.camera.core.ImageAnalysis diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt index c324c2bdf..cb6f5bd0d 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/scan_uri/ScanUriRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialNavigationApi::class, ExperimentalMaterialApi::class, ExperimentalMaterialNavigationApi::class) -package com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.scan_uri +package com.reown.sample.wallet.ui.routes.bottomsheet_routes.scan_uri import android.Manifest import android.content.pm.PackageManager @@ -54,8 +54,8 @@ import androidx.core.content.ContextCompat import androidx.navigation.NavController import com.google.accompanist.navigation.material.BottomSheetNavigatorSheetState import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.generated.CloseButton +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.generated.CloseButton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt index 1b66d9ef0..09b238b0c 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialNavigationApi::class) -package com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.update_subscription +package com.reown.sample.wallet.ui.routes.bottomsheet_routes.update_subscription import android.widget.Toast import androidx.compose.animation.AnimatedVisibility @@ -44,8 +44,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.google.accompanist.navigation.material.BottomSheetNavigatorSheetState import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.walletconnect.sample.common.ui.theme.blue_accent -import com.walletconnect.sample.wallet.ui.routes.showSnackbar +import com.reown.sample.common.ui.theme.blue_accent +import com.reown.sample.wallet.ui.routes.showSnackbar import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt similarity index 88% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt index 9d9cf1209..017b7349b 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/bottomsheet_routes/update_subscription/UpdateSubscriptionViewModel.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.update_subscription +package com.reown.sample.wallet.ui.routes.bottomsheet_routes.update_subscription import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.NotifyDelegate -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.toUI +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.NotifyDelegate +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.toUI import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt similarity index 93% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt index 6edbf2b93..6ee14c488 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connection_details/ConnectionDetailsRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalPagerApi::class, ExperimentalPagerApi::class, ExperimentalPagerApi::class) -package com.walletconnect.sample.wallet.ui.routes.composable_routes.connection_details +package com.reown.sample.wallet.ui.routes.composable_routes.connection_details import android.widget.Toast import androidx.compose.animation.AnimatedContent @@ -53,18 +53,18 @@ import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.Content -import com.walletconnect.sample.wallet.ui.common.InnerContent -import com.walletconnect.sample.wallet.ui.common.blue.BlueLabelTexts -import com.walletconnect.sample.wallet.ui.common.getAllEventsByChainId -import com.walletconnect.sample.wallet.ui.common.getAllMethodsByChainId -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionType -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionUI -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.Content +import com.reown.sample.wallet.ui.common.InnerContent +import com.reown.sample.wallet.ui.common.blue.BlueLabelTexts +import com.reown.sample.wallet.ui.common.getAllEventsByChainId +import com.reown.sample.wallet.ui.common.getAllMethodsByChainId +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionType +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionUI +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -91,7 +91,7 @@ fun ConnectionDetailsRoute(navController: NavController, connectionId: Int?, con val lastDelimiterIndex = account.indexOfLast { it == ':' } val chainId = account.dropLast(account.lastIndex - lastDelimiterIndex + 1) val event = getAllEventsByChainId(uiConnection.type.namespaces.values.first(), account).first() - Web3Wallet.emitSessionEvent( + WalletKit.emitSessionEvent( Wallet.Params.SessionEmit(uiConnection.type.topic, Wallet.Model.SessionEvent(event, "someData"), chainId), onSuccess = { isEmitLoading = false @@ -133,7 +133,7 @@ fun ConnectionDetailsRoute(navController: NavController, connectionId: Int?, con ) ).toMutableMap() val params = Wallet.Params.SessionUpdate(uiConnection.type.topic, newNamespaces) - Web3Wallet.updateSession(params, + WalletKit.updateSession(params, onSuccess = { isUpdateLoading = false connectionsViewModel.refreshConnections() @@ -169,7 +169,7 @@ fun ConnectionDetailsRoute(navController: NavController, connectionId: Int?, con is ConnectionType.Sign -> { try { isDeleteLoading = true - Web3Wallet.disconnectSession(Wallet.Params.SessionDisconnect(uiConnection.type.topic), + WalletKit.disconnectSession(Wallet.Params.SessionDisconnect(uiConnection.type.topic), onSuccess = { isDeleteLoading = false connectionsViewModel.refreshConnections() @@ -208,7 +208,7 @@ fun ConnectionDetailsRoute(navController: NavController, connectionId: Int?, con val chainId = "$namespace:$reference" val accountsToChange = connectionsViewModel.getAccountsToChange() - Web3Wallet.emitSessionEvent(Wallet.Params.SessionEmit(uiConnection.type.topic, Wallet.Model.SessionEvent("accountsChanged", accountsToChange), chainId), + WalletKit.emitSessionEvent(Wallet.Params.SessionEmit(uiConnection.type.topic, Wallet.Model.SessionEvent("accountsChanged", accountsToChange), chainId), onSuccess = { composableScope.launch(Dispatchers.Main) { Toast.makeText(context, "Switching account", Toast.LENGTH_SHORT).show() @@ -354,7 +354,7 @@ fun Connection(connectionUI: ConnectionUI) { .data(connectionUI.icon) .size(60) .crossfade(true) - .error(com.walletconnect.sample.common.R.drawable.ic_walletconnect_circle_blue) + .error(com.reown.sample.common.R.drawable.ic_walletconnect_circle_blue) .listener( onSuccess = { request, metadata -> println("onSuccess: $request, $metadata") }, onError = { _, throwable -> println("Error loading image: ${throwable.throwable.message}") }) @@ -391,7 +391,7 @@ fun TopButtons(navController: NavController, isEmitAndUpdateVisible: Boolean, is .clickable(interactionSource = interactionSourceRow, indication = null) { navController.popBackStack() } .padding(5.dp) ) { - Icon(tint = color, imageVector = ImageVector.vectorResource(id = com.walletconnect.sample.common.R.drawable.chevron_left), contentDescription = "Go back") + Icon(tint = color, imageVector = ImageVector.vectorResource(id = com.reown.sample.common.R.drawable.chevron_left), contentDescription = "Go back") Spacer(modifier = Modifier.width(5.dp)) Text(text = "Connections", style = style) } diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt new file mode 100644 index 000000000..0403e8a82 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt @@ -0,0 +1,16 @@ +package com.reown.sample.wallet.ui.routes.composable_routes.connections + +import com.reown.walletkit.client.Wallet + +data class ConnectionUI( + val id: Int, + val type: ConnectionType, + val name: String, + val uri: String, + val icon: String?, +) + +sealed class ConnectionType { + data class Sign(val topic: String, val namespaces: Map) : ConnectionType() +} + diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt index e59a9e8d4..ec8196e4a 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsButton.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.connections +package com.reown.sample.wallet.ui.routes.composable_routes.connections import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -18,7 +18,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.walletconnect.sample.wallet.R +import com.reown.sample.wallet.R @Composable fun ConnectionsButton( diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt similarity index 89% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt index 0513d96d7..d78c81fa0 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.connections +package com.reown.sample.wallet.ui.routes.composable_routes.connections import android.content.Intent import androidx.compose.foundation.Image @@ -40,25 +40,16 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavController import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest -import com.walletconnect.sample.common.ui.TopBarActionImage -import com.walletconnect.sample.common.ui.WCTopAppBar -import com.walletconnect.sample.common.ui.findActivity -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.Web3WalletViewModel -import com.walletconnect.sample.wallet.ui.routes.Route +import com.reown.sample.common.ui.TopBarActionImage +import com.reown.sample.common.ui.WCTopAppBar +import com.reown.sample.common.ui.findActivity +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.Web3WalletViewModel +import com.reown.sample.wallet.ui.routes.Route @Composable fun ConnectionsRoute(navController: NavController, connectionsViewModel: ConnectionsViewModel, web3WalletViewModel: Web3WalletViewModel) { - val context = LocalContext.current - val activity = context.findActivity() - - activity?.intent.takeIf { intent -> intent?.action == Intent.ACTION_VIEW && !intent.dataString.isNullOrBlank() }?.let { intent -> - if (intent.dataString?.startsWith("kotlin-web3wallet://wc?uri=wc") == true) { - web3WalletViewModel.pair(intent.dataString.toString()) - } - intent.data = null - } connectionsViewModel.refreshConnections() val connections by connectionsViewModel.connections.collectAsState(initial = emptyList()) @@ -147,7 +138,7 @@ fun Connection( .data(connectionUI.icon) .size(60) .crossfade(true) - .error(com.walletconnect.sample.common.R.drawable.ic_walletconnect_circle_blue) + .error(com.reown.sample.common.R.drawable.ic_walletconnect_circle_blue) .listener( onSuccess = { request, metadata -> println("onSuccess: $request, $metadata") }, onError = { _, throwable -> diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt similarity index 88% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt index c64460d88..6f658c851 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/connections/ConnectionsViewModel.kt @@ -1,14 +1,14 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.connections +package com.reown.sample.wallet.ui.routes.composable_routes.connections import android.util.Log import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.walletconnect.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS -import com.walletconnect.sample.wallet.domain.ACCOUNTS_2_EIP155_ADDRESS -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.web3.wallet.client.Web3Wallet +import com.reown.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS +import com.reown.sample.wallet.domain.ACCOUNTS_2_EIP155_ADDRESS +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.walletkit.client.WalletKit import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -65,7 +65,7 @@ class ConnectionsViewModel : ViewModel() { private fun getLatestActiveSignSessions(): List { return try { - Web3Wallet.getListOfActiveSessions().filter { wcSession -> + WalletKit.getListOfActiveSessions().filter { wcSession -> wcSession.metaData != null }.mapIndexed { index, wcSession -> ConnectionUI( diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt similarity index 85% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt index a69114426..3f2764a9a 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/get_started/GetStartedRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.get_started +package com.reown.sample.wallet.ui.routes.composable_routes.get_started import android.app.Activity import android.content.Context @@ -16,11 +16,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.walletconnect.sample.wallet.ui.common.generated.GetStarted -import com.walletconnect.sample.wallet.ui.common.generated.Welcome -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.common.ui.findActivity +import com.reown.sample.wallet.ui.common.generated.GetStarted +import com.reown.sample.wallet.ui.common.generated.Welcome +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.R +import com.reown.sample.common.ui.findActivity import timber.log.Timber @Composable diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt similarity index 92% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt index 444d5c471..02bc6e66a 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalFoundationApi::class) -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox +package com.reown.sample.wallet.ui.routes.composable_routes.inbox import android.widget.Toast import androidx.compose.foundation.ExperimentalFoundationApi @@ -34,17 +34,17 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import com.walletconnect.notify.client.InvalidDidJsonFileException -import com.walletconnect.sample.common.ui.WCTopAppBar -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.UiModePreview -import com.walletconnect.sample.common.ui.theme.blue_accent -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.ImageUrl -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.discover.DiscoverTab -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.discover.ExplorerApp -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.subscriptions.SubscriptionsTab +import com.reown.notify.client.InvalidDidJsonFileException +import com.reown.sample.common.ui.WCTopAppBar +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.UiModePreview +import com.reown.sample.common.ui.theme.blue_accent +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.discover.DiscoverTab +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.discover.ExplorerApp +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.subscriptions.SubscriptionsTab import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt similarity index 91% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt index 738c48fc1..ea6b487cf 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/InboxViewModel.kt @@ -1,21 +1,21 @@ @file:OptIn(FlowPreview::class) -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox +package com.reown.sample.wallet.ui.routes.composable_routes.inbox import android.app.Application import androidx.core.net.toUri import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.walletconnect.android.CoreClient -import com.walletconnect.android.internal.common.explorer.data.model.Project -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.NotifyDelegate -import com.walletconnect.sample.wallet.ui.common.ImageUrl -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.toUI -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.discover.ExplorerApp +import com.reown.android.CoreClient +import com.reown.android.internal.common.explorer.data.model.Project +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.NotifyDelegate +import com.reown.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.toUI +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.discover.ExplorerApp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber import kotlin.time.Duration.Companion.seconds -import com.walletconnect.android.internal.common.explorer.data.model.ImageUrl as WCImageUrl +import com.reown.android.internal.common.explorer.data.model.ImageUrl as WCImageUrl class InboxViewModel(application: Application) : AndroidViewModel(application) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt index e5832fa94..50b59ea59 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/LazyColumnSurroundedWithFogVertically.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox +package com.reown.sample.wallet.ui.routes.composable_routes.inbox import androidx.compose.animation.AnimatedVisibility diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt similarity index 95% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt index 5c3b5e12b..bcff3e097 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/DiscoverTab.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.discover +package com.reown.sample.wallet.ui.routes.composable_routes.inbox.discover import android.content.Intent @@ -45,11 +45,11 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.common.ui.theme.blue_accent -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.subscriptions.SubscriptionIcon -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.DiscoverState -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically +import com.reown.sample.common.ui.theme.blue_accent +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.subscriptions.SubscriptionIcon +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.DiscoverState +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically import java.net.URI diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt similarity index 77% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt index a72bb6a6f..81a80d416 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/discover/ExplorerApp.kt @@ -1,6 +1,6 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.discover +package com.reown.sample.wallet.ui.routes.composable_routes.inbox.discover -import com.walletconnect.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.ImageUrl data class ExplorerApp( diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt similarity index 93% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt index 794577210..d38776b8c 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/ActiveSubscriptions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.subscriptions +package com.reown.sample.wallet.ui.routes.composable_routes.inbox.subscriptions import androidx.compose.foundation.background @@ -32,11 +32,11 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController -import com.walletconnect.sample.wallet.ui.common.ImageUrl -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.SubscriptionIcon -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically +import com.reown.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.SubscriptionIcon +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically @Composable diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt similarity index 97% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt index c4e5adc5b..241e09275 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/NoActiveSubscriptions.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.subscriptions +package com.reown.sample.wallet.ui.routes.composable_routes.inbox.subscriptions import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween @@ -40,7 +40,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.R +import com.reown.sample.wallet.R import kotlinx.coroutines.delay @Composable diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt new file mode 100644 index 000000000..8067d98dd --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt @@ -0,0 +1,34 @@ +package com.reown.sample.wallet.ui.routes.composable_routes.inbox.subscriptions + + +import androidx.compose.runtime.Composable +import androidx.navigation.NavHostController +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.SubscriptionsState + + +@Composable +fun SubscriptionsTab( + navController: NavHostController, + state: SubscriptionsState, + activeSubscriptions: List, + onDiscoverMoreClicked: () -> Unit, +) { + when (state) { + is SubscriptionsState.Failure -> { + + } + + is SubscriptionsState.Unsubscribed -> { + NoActiveSubscriptions(onDiscoverMoreClicked) + } + + is SubscriptionsState.Searching -> { + + } + + SubscriptionsState.Success -> { + ActiveSubscriptions(navController, activeSubscriptions) + } + } +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt similarity index 92% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt index 653e040d4..11c71985d 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsHeader.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.notifications +package com.reown.sample.wallet.ui.routes.composable_routes.notifications import androidx.compose.foundation.Image @@ -35,9 +35,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.SubscriptionIcon +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.SubscriptionIcon @Composable @@ -64,7 +64,7 @@ fun NotificationsHeader( modifier = Modifier .width(20.dp) .height(28.dp) - .clickable(indication = rememberRipple(bounded = false, radius = 20.dp), interactionSource = remember { MutableInteractionSource() }, onClick = onBackIconClick) + .clickable(onClick = onBackIconClick) .padding(vertical = 8.dp), imageVector = ImageVector.vectorResource(id = R.drawable.ic_chevron_left), contentDescription = "Back arrow", diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt index 75fab4425..d4075a88c 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsOptionsMenu.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.notifications +package com.reown.sample.wallet.ui.routes.composable_routes.notifications import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -34,7 +34,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.R +import com.reown.sample.wallet.R @Composable fun NotificationsOptionsMenu( @@ -54,7 +54,7 @@ fun NotificationsOptionsMenu( Icon( modifier = Modifier .size(20.dp) - .clickable(indication = rememberRipple(bounded = false, radius = 20.dp), interactionSource = remember { MutableInteractionSource() }, onClick = onMoreIconClick), + .clickable(onClick = onMoreIconClick), painter = painterResource(id = R.drawable.ic_more), contentDescription = "More", ) diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt index 131f06d98..50528ab5e 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificationsViewModel.kt @@ -1,17 +1,17 @@ @file:OptIn(FlowPreview::class) -package com.walletconnect.sample.wallet.ui.routes.composable_routes.notifications +package com.reown.sample.wallet.ui.routes.composable_routes.notifications import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.NotifyDelegate -import com.walletconnect.sample.wallet.domain.model.NotificationUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.toUI +import com.reown.notify.client.Notify +import com.reown.notify.client.NotifyClient +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.NotifyDelegate +import com.reown.sample.wallet.domain.model.NotificationUI +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.toUI import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt index 3297669ae..c9517b3a2 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/notifications/NotificiationsScreenRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.notifications +package com.reown.sample.wallet.ui.routes.composable_routes.notifications import android.content.Intent import android.net.Uri @@ -58,18 +58,18 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import com.walletconnect.sample.common.ui.commons.ButtonWithLoader -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.UiModePreview -import com.walletconnect.sample.common.ui.theme.blue_accent -import com.walletconnect.sample.wallet.domain.model.NotificationUI -import com.walletconnect.sample.wallet.ui.common.ImageUrl -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.common.subscriptions.NotificationIcon -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.InboxViewModel -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically -import com.walletconnect.sample.wallet.ui.routes.showSnackbar +import com.reown.sample.common.ui.commons.ButtonWithLoader +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.UiModePreview +import com.reown.sample.common.ui.theme.blue_accent +import com.reown.sample.wallet.domain.model.NotificationUI +import com.reown.sample.wallet.ui.common.ImageUrl +import com.reown.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI +import com.reown.sample.wallet.ui.common.subscriptions.NotificationIcon +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.InboxViewModel +import com.reown.sample.wallet.ui.routes.composable_routes.inbox.LazyColumnSurroundedWithFogVertically +import com.reown.sample.wallet.ui.routes.showSnackbar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt similarity index 76% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt index 247be6035..375ba6849 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingUI.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.settings +package com.reown.sample.wallet.ui.routes.composable_routes.settings sealed interface Section { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt similarity index 95% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt index 33ee14def..892cc2614 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.settings +package com.reown.sample.wallet.ui.routes.composable_routes.settings import android.content.Context import android.widget.Toast @@ -40,11 +40,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import com.walletconnect.sample.common.ui.WCTopAppBar -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.UiModePreview -import com.walletconnect.sample.wallet.BuildConfig -import com.walletconnect.sample.wallet.R +import com.reown.sample.common.ui.WCTopAppBar +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.UiModePreview +import com.reown.sample.wallet.BuildConfig +import com.reown.sample.wallet.R @Composable fun SettingsRoute(navController: NavHostController) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt similarity index 79% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt index 9a4e24c78..365ed1491 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/composable_routes/settings/SettingsViewModel.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.settings +package com.reown.sample.wallet.ui.routes.composable_routes.settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.firebase.messaging.FirebaseMessaging -import com.walletconnect.android.CoreClient -import com.walletconnect.sample.wallet.domain.EthAccountDelegate +import com.reown.android.CoreClient +import com.reown.sample.wallet.domain.EthAccountDelegate import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt similarity index 96% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt index a63976575..a55699b80 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/paste_uri/PasteUriRoute.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalComposeUiApi::class) -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.paste_uri +package com.reown.sample.wallet.ui.routes.dialog_routes.paste_uri import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -24,8 +24,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.common.ui.themedColor @Composable fun PasteUriRoute(onSubmit: (String) -> Unit) { diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt similarity index 92% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt index 5996a920c..66b4ef496 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_authenticate +package com.reown.sample.wallet.ui.routes.dialog_routes.session_authenticate import android.content.Context import android.net.Uri @@ -36,18 +36,18 @@ import androidx.compose.ui.unit.sp import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.sample.common.sendResponseDeepLink -import com.walletconnect.sample.common.ui.theme.mismatch_color -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.Buttons -import com.walletconnect.sample.wallet.ui.common.SemiTransparentDialog -import com.walletconnect.sample.wallet.ui.common.generated.CancelButton -import com.walletconnect.sample.wallet.ui.common.peer.Peer -import com.walletconnect.sample.wallet.ui.common.peer.getValidationColor -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.sample.common.sendResponseDeepLink +import com.reown.sample.common.ui.theme.mismatch_color +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.Buttons +import com.reown.sample.wallet.ui.common.SemiTransparentDialog +import com.reown.sample.wallet.ui.common.generated.CancelButton +import com.reown.sample.wallet.ui.common.peer.Peer +import com.reown.sample.wallet.ui.common.peer.getValidationColor +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt similarity index 83% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt index 57a2cea29..36a8ec10b 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateUI.kt @@ -1,9 +1,9 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_authenticate +package com.reown.sample.wallet.ui.routes.dialog_routes.session_authenticate -import com.walletconnect.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS -import com.walletconnect.sample.wallet.ui.common.peer.PeerContextUI -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.web3.wallet.client.Wallet +import com.reown.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS +import com.reown.sample.wallet.ui.common.peer.PeerContextUI +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.walletkit.client.Wallet data class SessionAuthenticateUI( val peerUI: PeerUI, diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt similarity index 79% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt index 5ef1a7426..9bdb94af8 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_authenticate/SessionAuthenticateViewModel.kt @@ -1,20 +1,20 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_authenticate +package com.reown.sample.wallet.ui.routes.dialog_routes.session_authenticate import androidx.lifecycle.ViewModel import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.sample.wallet.ui.common.peer.toPeerUI -import com.walletconnect.util.hexToBytes -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet -import com.walletconnect.web3.wallet.utils.CacaoSigner +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.android.utils.cacao.sign +import com.reown.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.sample.wallet.ui.common.peer.toPeerUI +import com.reown.util.hexToBytes +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit +import com.reown.walletkit.utils.CacaoSigner class SessionAuthenticateViewModel : ViewModel() { val sessionAuthenticateUI: SessionAuthenticateUI? get() = generateAuthRequestUI() @@ -26,7 +26,7 @@ class SessionAuthenticateViewModel : ViewModel() { val auths = mutableListOf() val authPayloadParams = - Web3Wallet.generateAuthPayloadParams( + WalletKit.generateAuthPayloadParams( sessionAuthenticate.payloadParams, supportedChains = listOf("eip155:1", "eip155:137", "eip155:56"), supportedMethods = listOf("personal_sign", "eth_signTypedData", "eth_signTypedData_v4", "eth_sign") @@ -35,14 +35,14 @@ class SessionAuthenticateViewModel : ViewModel() { authPayloadParams.chains .forEach { chain -> val issuer = "did:pkh:$chain:$ACCOUNTS_1_EIP155_ADDRESS" - val message = Web3Wallet.formatAuthMessage(Wallet.Params.FormatAuthMessage(authPayloadParams, issuer)) + val message = WalletKit.formatAuthMessage(Wallet.Params.FormatAuthMessage(authPayloadParams, issuer)) val signature = CacaoSigner.sign(message, EthAccountDelegate.privateKey.hexToBytes(), SignatureType.EIP191) - val auth = Web3Wallet.generateAuthObject(authPayloadParams, issuer, signature) + val auth = WalletKit.generateAuthObject(authPayloadParams, issuer, signature) auths.add(auth) } val approveProposal = Wallet.Params.ApproveSessionAuthenticate(id = sessionAuthenticate.id, auths = auths) - Web3Wallet.approveSessionAuthenticate(approveProposal, + WalletKit.approveSessionAuthenticate(approveProposal, onSuccess = { WCDelegate.sessionAuthenticateEvent = null onSuccess(sessionAuthenticate.participant.metadata?.redirect ?: "") @@ -75,7 +75,7 @@ class SessionAuthenticateViewModel : ViewModel() { reason = rejectionReason ) - Web3Wallet.rejectSessionAuthenticate(reject, + WalletKit.rejectSessionAuthenticate(reject, onSuccess = { WCDelegate.sessionAuthenticateEvent = null onSuccess(sessionAuthenticate.participant.metadata?.redirect ?: "") @@ -105,7 +105,7 @@ class SessionAuthenticateViewModel : ViewModel() { .forEach { chain -> val issuer = "did:pkh:$chain:$ACCOUNTS_1_EIP155_ADDRESS" val message = try { - Web3Wallet.formatAuthMessage(Wallet.Params.FormatAuthMessage(sessionAuthenticate.payloadParams, issuer)) + WalletKit.formatAuthMessage(Wallet.Params.FormatAuthMessage(sessionAuthenticate.payloadParams, issuer)) } catch (e: Exception) { "Invalid message, error: ${e.message}" } diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt similarity index 92% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt index 2b55eb980..ec8f93f03 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_proposal +package com.reown.sample.wallet.ui.routes.dialog_routes.session_proposal import android.content.Context import android.net.Uri @@ -41,25 +41,25 @@ import androidx.core.net.toUri import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.sample.common.Chains -import com.walletconnect.sample.common.CompletePreviews -import com.walletconnect.sample.common.sendResponseDeepLink -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.mismatch_color -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.common.Buttons -import com.walletconnect.sample.wallet.ui.common.SemiTransparentDialog -import com.walletconnect.sample.wallet.ui.common.generated.CancelButton -import com.walletconnect.sample.wallet.ui.common.peer.Peer -import com.walletconnect.sample.wallet.ui.common.peer.PeerContextUI -import com.walletconnect.sample.wallet.ui.common.peer.Validation -import com.walletconnect.sample.wallet.ui.common.peer.getColor -import com.walletconnect.sample.wallet.ui.common.peer.getDescriptionContent -import com.walletconnect.sample.wallet.ui.common.peer.getDescriptionTitle -import com.walletconnect.sample.wallet.ui.common.peer.getValidationIcon -import com.walletconnect.sample.wallet.ui.routes.Route +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.sample.common.Chains +import com.reown.sample.common.CompletePreviews +import com.reown.sample.common.sendResponseDeepLink +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.mismatch_color +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.common.Buttons +import com.reown.sample.wallet.ui.common.SemiTransparentDialog +import com.reown.sample.wallet.ui.common.generated.CancelButton +import com.reown.sample.wallet.ui.common.peer.Peer +import com.reown.sample.wallet.ui.common.peer.PeerContextUI +import com.reown.sample.wallet.ui.common.peer.Validation +import com.reown.sample.wallet.ui.common.peer.getColor +import com.reown.sample.wallet.ui.common.peer.getDescriptionContent +import com.reown.sample.wallet.ui.common.peer.getDescriptionTitle +import com.reown.sample.wallet.ui.common.peer.getValidationIcon +import com.reown.sample.wallet.ui.routes.Route import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt similarity index 87% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt index a8728f9f3..6ae3a9208 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalUI.kt @@ -1,10 +1,10 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_proposal +package com.reown.sample.wallet.ui.routes.dialog_routes.session_proposal -import com.walletconnect.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS -import com.walletconnect.sample.wallet.domain.ACCOUNTS_2_EIP155_ADDRESS -import com.walletconnect.sample.wallet.ui.common.peer.PeerContextUI -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.web3.wallet.client.Wallet +import com.reown.sample.wallet.domain.ACCOUNTS_1_EIP155_ADDRESS +import com.reown.sample.wallet.domain.ACCOUNTS_2_EIP155_ADDRESS +import com.reown.sample.wallet.ui.common.peer.PeerContextUI +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.walletkit.client.Wallet data class SessionProposalUI( val peerUI: PeerUI, diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt similarity index 80% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt index 63c3388df..70525b9a6 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_proposal/SessionProposalViewModel.kt @@ -1,26 +1,26 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_proposal +package com.reown.sample.wallet.ui.routes.dialog_routes.session_proposal import androidx.lifecycle.ViewModel import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.sample.wallet.ui.common.peer.toPeerUI -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.sample.wallet.ui.common.peer.toPeerUI +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit import timber.log.Timber class SessionProposalViewModel : ViewModel() { val sessionProposal: SessionProposalUI? = generateSessionProposalUI() fun approve(proposalPublicKey: String, onSuccess: (String) -> Unit = {}, onError: (Throwable) -> Unit = {}) { - val proposal = Web3Wallet.getSessionProposals().find { it.proposerPublicKey == proposalPublicKey } + val proposal = WalletKit.getSessionProposals().find { it.proposerPublicKey == proposalPublicKey } if (proposal != null) { try { Timber.d("Approving session proposal: $proposalPublicKey") - val sessionNamespaces = Web3Wallet.generateApprovedNamespaces(sessionProposal = proposal, supportedNamespaces = walletMetaData.namespaces) + val sessionNamespaces = WalletKit.generateApprovedNamespaces(sessionProposal = proposal, supportedNamespaces = walletMetaData.namespaces) val approveProposal = Wallet.Params.SessionApprove(proposerPublicKey = proposal.proposerPublicKey, namespaces = sessionNamespaces) - Web3Wallet.approveSession(approveProposal, + WalletKit.approveSession(approveProposal, onError = { error -> Firebase.crashlytics.recordException(error.throwable) WCDelegate.sessionProposalEvent = null @@ -41,7 +41,7 @@ class SessionProposalViewModel : ViewModel() { } fun reject(proposalPublicKey: String, onSuccess: (String) -> Unit = {}, onError: (Throwable) -> Unit = {}) { - val proposal = Web3Wallet.getSessionProposals().find { it.proposerPublicKey == proposalPublicKey } + val proposal = WalletKit.getSessionProposals().find { it.proposerPublicKey == proposalPublicKey } if (proposal != null) { try { val rejectionReason = "Reject Session" @@ -50,7 +50,7 @@ class SessionProposalViewModel : ViewModel() { reason = rejectionReason ) - Web3Wallet.rejectSession(reject, + WalletKit.rejectSession(reject, onSuccess = { WCDelegate.sessionProposalEvent = null onSuccess(proposal.redirect) diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt similarity index 90% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt index 91326db6b..55d1bb1d8 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_request +package com.reown.sample.wallet.ui.routes.dialog_routes.session_request import android.annotation.SuppressLint import android.content.Context @@ -32,22 +32,22 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.sample.common.CompletePreviews -import com.walletconnect.sample.common.sendResponseDeepLink -import com.walletconnect.sample.common.ui.theme.PreviewTheme -import com.walletconnect.sample.common.ui.theme.verified_color -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.domain.WCDelegate.currentId -import com.walletconnect.sample.wallet.ui.common.Buttons -import com.walletconnect.sample.wallet.ui.common.Content -import com.walletconnect.sample.wallet.ui.common.InnerContent -import com.walletconnect.sample.wallet.ui.common.SemiTransparentDialog -import com.walletconnect.sample.wallet.ui.common.blue.BlueLabelRow -import com.walletconnect.sample.wallet.ui.common.peer.Peer -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.sample.wallet.ui.common.peer.getColor -import com.walletconnect.sample.wallet.ui.routes.Route +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.sample.common.CompletePreviews +import com.reown.sample.common.sendResponseDeepLink +import com.reown.sample.common.ui.theme.PreviewTheme +import com.reown.sample.common.ui.theme.verified_color +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.domain.WCDelegate.currentId +import com.reown.sample.wallet.ui.common.Buttons +import com.reown.sample.wallet.ui.common.Content +import com.reown.sample.wallet.ui.common.InnerContent +import com.reown.sample.wallet.ui.common.SemiTransparentDialog +import com.reown.sample.wallet.ui.common.blue.BlueLabelRow +import com.reown.sample.wallet.ui.common.peer.Peer +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.sample.wallet.ui.common.peer.getColor +import com.reown.sample.wallet.ui.routes.Route import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt new file mode 100644 index 000000000..4936c7e87 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt @@ -0,0 +1,18 @@ +package com.reown.sample.wallet.ui.routes.dialog_routes.session_request + +import com.reown.sample.wallet.ui.common.peer.PeerContextUI +import com.reown.sample.wallet.ui.common.peer.PeerUI + +sealed class SessionRequestUI { + object Initial : SessionRequestUI() + + data class Content( + val peerUI: PeerUI, + val topic: String, + val requestId: Long, + val param: String, + val chain: String?, + val method: String, + val peerContextUI: PeerContextUI + ) : SessionRequestUI() +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt similarity index 85% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt index b3f499504..19f15b78a 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestViewModel.kt @@ -1,22 +1,22 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_request +package com.reown.sample.wallet.ui.routes.dialog_routes.session_request import android.net.Uri import androidx.core.net.toUri import androidx.lifecycle.ViewModel import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.exception.NoConnectivityException -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.sample.common.Chains -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.sample.wallet.ui.common.peer.toPeerUI -import com.walletconnect.util.hexToBytes -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet -import com.walletconnect.web3.wallet.utils.CacaoSigner +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.exception.NoConnectivityException +import com.reown.android.utils.cacao.sign +import com.reown.sample.common.Chains +import com.reown.sample.wallet.domain.EthAccountDelegate +import com.reown.sample.wallet.domain.WCDelegate +import com.reown.sample.wallet.ui.common.peer.PeerUI +import com.reown.sample.wallet.ui.common.peer.toPeerUI +import com.reown.util.hexToBytes +import com.reown.walletkit.client.Wallet +import com.reown.walletkit.client.WalletKit +import com.reown.walletkit.utils.CacaoSigner import org.json.JSONArray import org.web3j.utils.Numeric.hexStringToByteArray @@ -41,8 +41,8 @@ class SessionRequestViewModel : ViewModel() { message = "Kotlin Wallet Error" ) ) - val redirect = Web3Wallet.getActiveSessionByTopic(sessionRequest.topic)?.redirect?.toUri() - Web3Wallet.respondSessionRequest(result, + val redirect = WalletKit.getActiveSessionByTopic(sessionRequest.topic)?.redirect?.toUri() + WalletKit.respondSessionRequest(result, onSuccess = { clearSessionRequest() onSuccess(redirect) @@ -107,8 +107,8 @@ class SessionRequestViewModel : ViewModel() { ) ) - val redirect = Web3Wallet.getActiveSessionByTopic(sessionRequest.topic)?.redirect?.toUri() - Web3Wallet.respondSessionRequest(response, + val redirect = WalletKit.getActiveSessionByTopic(sessionRequest.topic)?.redirect?.toUri() + WalletKit.respondSessionRequest(response, onSuccess = { clearSessionRequest() onSuccess(redirect) diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt similarity index 89% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt index 9efa35238..34d5a22c6 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/snackbar_message/SnackbarMessageRoute.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.snackbar_message +package com.reown.sample.wallet.ui.routes.dialog_routes.snackbar_message import androidx.compose.foundation.layout.padding import androidx.compose.material.Button @@ -15,8 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.util.Empty +import com.reown.sample.common.ui.themedColor +import com.reown.util.Empty import kotlinx.coroutines.delay @Composable diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/BottomBar.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/BottomBar.kt similarity index 94% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/BottomBar.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/BottomBar.kt index 2b005b126..edac40495 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/BottomBar.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/BottomBar.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.routes.host +package com.reown.sample.wallet.ui.routes.host import androidx.annotation.DrawableRes import androidx.compose.foundation.Canvas @@ -22,9 +22,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState -import com.walletconnect.sample.common.ui.theme.blue_accent -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.routes.Route +import com.reown.sample.common.ui.theme.blue_accent +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.routes.Route enum class BottomBarItem(val route: Route, val label: String, @DrawableRes val icon: Int) { CONNECTIONS(Route.Connections, "Connections", R.drawable.ic_connections), diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/WalletSampleHost.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/WalletSampleHost.kt similarity index 92% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/WalletSampleHost.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/WalletSampleHost.kt index d5d18566c..25c62c996 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/host/WalletSampleHost.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/host/WalletSampleHost.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterialNavigationApi::class) -package com.walletconnect.sample.wallet.ui.routes.host +package com.reown.sample.wallet.ui.routes.host import android.widget.Toast import androidx.compose.foundation.Image @@ -49,14 +49,14 @@ import androidx.navigation.compose.currentBackStackEntryAsState import com.google.accompanist.navigation.material.BottomSheetNavigator import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi import com.pandulapeter.beagle.DebugMenuView -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.R -import com.walletconnect.sample.wallet.ui.Web3WalletNavGraph -import com.walletconnect.sample.wallet.ui.Web3WalletViewModel -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel -import com.walletconnect.sample.wallet.ui.state.ConnectionState -import com.walletconnect.sample.wallet.ui.state.PairingEvent +import com.reown.sample.common.ui.themedColor +import com.reown.sample.wallet.R +import com.reown.sample.wallet.ui.Web3WalletNavGraph +import com.reown.sample.wallet.ui.Web3WalletViewModel +import com.reown.sample.wallet.ui.routes.Route +import com.reown.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel +import com.reown.sample.wallet.ui.state.ConnectionState +import com.reown.sample.wallet.ui.state.PairingEvent import kotlinx.coroutines.delay @OptIn(ExperimentalMaterialNavigationApi::class) @@ -108,7 +108,7 @@ fun WalletSampleHost( ) if (connectionState is ConnectionState.Error) { - ErrorBanner() + ErrorBanner((connectionState as ConnectionState.Error).message) } else if (connectionState is ConnectionState.Ok) { RestoredConnectionBanner() } @@ -185,11 +185,11 @@ private fun BoxScope.Loader(initMessage: String, updateMessage: String) { } @Composable -private fun ErrorBanner() { +private fun ErrorBanner(message: String) { var shouldShow by remember { mutableStateOf(true) } LaunchedEffect(key1 = Unit) { - delay(2000) + delay(5000) shouldShow = false } @@ -208,7 +208,7 @@ private fun ErrorBanner() { colorFilter = ColorFilter.tint(color = Color.White) ) Spacer(modifier = Modifier.width(4.dp)) - Text(text = "Network connection lost", color = Color.White) + Text(text = "Network connection lost: $message", color = Color.White) } } } diff --git a/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/ConnectionState.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/ConnectionState.kt new file mode 100644 index 000000000..d36974853 --- /dev/null +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/ConnectionState.kt @@ -0,0 +1,11 @@ +package com.reown.sample.wallet.ui.state + +import kotlinx.coroutines.flow.MutableStateFlow + +val connectionStateFlow: MutableStateFlow = MutableStateFlow(ConnectionState.Idle) + +sealed class ConnectionState() { + data class Error(val message: String) : ConnectionState() + object Ok : ConnectionState() + object Idle : ConnectionState() +} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/PairingEvent.kt b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/PairingEvent.kt similarity index 76% rename from sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/PairingEvent.kt rename to sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/PairingEvent.kt index 34a7ca494..ac5d9f9f9 100644 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/PairingEvent.kt +++ b/sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/state/PairingEvent.kt @@ -1,4 +1,4 @@ -package com.walletconnect.sample.wallet.ui.state +package com.reown.sample.wallet.ui.state sealed class PairingEvent { data class Error(val message: String) : PairingEvent() diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletApplication.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletApplication.kt deleted file mode 100644 index 27f84ec39..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletApplication.kt +++ /dev/null @@ -1,253 +0,0 @@ -package com.walletconnect.sample.wallet - -import android.app.Application -import android.content.ClipData -import android.content.ClipboardManager -import com.google.firebase.appdistribution.FirebaseAppDistribution -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase -import com.google.firebase.messaging.FirebaseMessaging -import com.mixpanel.android.mpmetrics.MixpanelAPI -import com.pandulapeter.beagle.Beagle -import com.pandulapeter.beagle.common.configuration.Placement -import com.pandulapeter.beagle.modules.DividerModule -import com.pandulapeter.beagle.modules.HeaderModule -import com.pandulapeter.beagle.modules.PaddingModule -import com.pandulapeter.beagle.modules.TextInputModule -import com.pandulapeter.beagle.modules.TextModule -import com.walletconnect.android.Core -import com.walletconnect.android.CoreClient -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.foundation.util.Logger -import com.walletconnect.notify.client.Notify -import com.walletconnect.notify.client.NotifyClient -import com.walletconnect.notify.client.cacao.CacaoSigner -import com.walletconnect.sample.common.initBeagle -import com.walletconnect.sample.common.tag -import com.walletconnect.sample.wallet.domain.EthAccountDelegate -import com.walletconnect.sample.wallet.domain.NotificationHandler -import com.walletconnect.sample.wallet.domain.NotifyDelegate -import com.walletconnect.sample.wallet.domain.mixPanel -import com.walletconnect.sample.wallet.ui.state.ConnectionState -import com.walletconnect.sample.wallet.ui.state.connectionStateFlow -import com.walletconnect.util.hexToBytes -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import org.koin.core.qualifier.named -import timber.log.Timber -import com.walletconnect.sample.common.BuildConfig as CommonBuildConfig - -class Web3WalletApplication : Application() { - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - private var addFirebaseBeagleModules: () -> Unit = {} - private lateinit var logger: Logger - - override fun onCreate() { - super.onCreate() - - EthAccountDelegate.application = this - - val projectId = BuildConfig.PROJECT_ID - val appMetaData = Core.Model.AppMetaData( - name = "Kotlin Wallet", - description = "Kotlin Wallet Implementation", - url = "https://web3modal-laboratory-git-chore-kotlin-assetlinks-walletconnect1.vercel.app", - icons = listOf("https://raw.githubusercontent.com/WalletConnect/walletconnect-assets/master/Icon/Gradient/Icon.png"), - redirect = "kotlin-web3wallet://request", - appLink = BuildConfig.WALLET_APP_LINK, - linkMode = true - ) - - CoreClient.initialize( - application = this, - projectId = projectId, - metaData = appMetaData, - onError = { error -> - Firebase.crashlytics.recordException(error.throwable) - println(error.throwable.stackTraceToString()) - scope.launch { - connectionStateFlow.emit(ConnectionState.Error(error.throwable.message ?: "")) - } - } - ) - - mixPanel = MixpanelAPI.getInstance(this, CommonBuildConfig.MIX_PANEL, true).apply { - identify(CoreClient.Push.clientId) - people.set("\$name", EthAccountDelegate.ethAddress) - } - - logger = wcKoinApp.koin.get(named(AndroidCommonDITags.LOGGER)) - logger.log("Account: ${EthAccountDelegate.account}") - - Web3Wallet.initialize(Wallet.Params.Init(core = CoreClient), - onSuccess = { - logger.log("Web3Wallet initialized") - }, - onError = { error -> - Firebase.crashlytics.recordException(error.throwable) - logger.error(error.throwable.stackTraceToString()) - }) - - NotifyClient.initialize( - init = Notify.Params.Init(CoreClient) - ) { error -> - Firebase.crashlytics.recordException(error.throwable) - logger.error(error.throwable.stackTraceToString()) - } - - FirebaseAppDistribution.getInstance().updateIfNewReleaseAvailable() - - registerAccount() - initializeBeagle() - - - - wcKoinApp.koin.get().plant(object : Timber.Tree() { - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - if (t != null) { - mixPanel.track("error: $t, message: $message") - } else { - mixPanel.track(message) - } - } - }) - - - // For testing purposes only - FirebaseMessaging.getInstance().token.addOnSuccessListener { token -> - addFirebaseBeagleModules = { - Web3Wallet.registerDeviceToken(firebaseAccessToken = token, enableEncrypted = true, - onSuccess = { - Timber.tag(tag(this)).e("Successfully registered firebase token for Web3Wallet") - }, - onError = { - logger.error("Error while registering firebase token for Web3Wallet: ${it.throwable}") - Firebase.crashlytics.recordException(Throwable("Error while registering firebase token for Web3Wallet: ${it.throwable}")) - }) - - Beagle.add( - PaddingModule(size = PaddingModule.Size.LARGE, id = "${token}Padding"), - placement = Placement.Below(id = CoreClient.Push.clientId) - ) - Beagle.add( - TextModule(text = token) { - (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("FMC_Token", token)) - }, - placement = Placement.Below(id = "${token}Padding") - ) - } - addFirebaseBeagleModules() - } - - handleNotifyMessages() - } - - private fun initializeBeagle() { - initBeagle( - this@Web3WalletApplication, - HeaderModule( - title = getString(R.string.app_name), - subtitle = BuildConfig.APPLICATION_ID, - text = "${BuildConfig.BUILD_TYPE} v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" - ), - DividerModule(), - TextModule(text = EthAccountDelegate.ethAddress) { - (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("Account", EthAccountDelegate.ethAddress)) - }, - PaddingModule(size = PaddingModule.Size.LARGE), - TextModule(text = EthAccountDelegate.privateKey) { - (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("Private Key", EthAccountDelegate.privateKey)) - }, - PaddingModule(size = PaddingModule.Size.LARGE), - TextModule(text = CoreClient.Push.clientId, id = CoreClient.Push.clientId) { - (getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(ClipData.newPlainText("ClientId", CoreClient.Push.clientId)) - }, - DividerModule(), - TextInputModule( - text = "Import Private Key", - areRealTimeUpdatesEnabled = false, - validator = { text -> - !text.startsWith("0x") && text.length == 64 - }, - onValueChanged = { text -> - NotifyClient.unregister( - params = Notify.Params.Unregister( - EthAccountDelegate.ethAddress, - ), - onSuccess = { - logger.log("Unregister Success") - EthAccountDelegate.privateKey = text - registerAccount() - }, - onError = { logger.error(it.throwable.stackTraceToString()) } - ) - } - ), - ) - addFirebaseBeagleModules() - } - - - private fun handleNotifyMessages() { - val scope = CoroutineScope(Dispatchers.Default) - - val notifyEventsJob = NotifyDelegate.notifyEvents - .filterIsInstance() - .onEach { notification -> NotificationHandler.addNotification(notification.notification) } - .launchIn(scope) - - - val notificationDisplayingJob = NotificationHandler.startNotificationDisplayingJob(scope, this) - - - notifyEventsJob.invokeOnCompletion { cause -> - onScopeCancelled(cause, "notifyEventsJob") - } - - notificationDisplayingJob.invokeOnCompletion { cause -> - onScopeCancelled(cause, "notificationDisplayingJob") - } - } - - private fun onScopeCancelled(error: Throwable?, job: String) { - wcKoinApp.koin.get(named(AndroidCommonDITags.LOGGER)).error("onScopeCancelled($job): $error") - } - - private fun registerAccount() { - val account = EthAccountDelegate.ethAddress - val domain = BuildConfig.APPLICATION_ID - val isRegistered = NotifyClient.isRegistered(params = Notify.Params.IsRegistered(account = account, domain = domain)) - - if (!isRegistered) { - NotifyClient.prepareRegistration( - params = Notify.Params.PrepareRegistration(account = account, domain = domain), - onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> - logger.log("PrepareRegistration Success: $cacaoPayloadWithIdentityPrivateKey") - - val signature = CacaoSigner.sign(message, EthAccountDelegate.privateKey.hexToBytes(), SignatureType.EIP191) - - NotifyClient.register( - params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), - onSuccess = { logger.log("Register Success") }, - onError = { logger.error(it.throwable.stackTraceToString()) } - ) - - }, - onError = { logger.error(it.throwable.stackTraceToString()) } - ) - } else { - logger.log("$account is already registered") - } - } - -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletMessageService.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletMessageService.kt deleted file mode 100644 index 11e3d22c3..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/Web3WalletMessageService.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.walletconnect.sample.wallet - -import android.annotation.SuppressLint -import com.google.firebase.messaging.RemoteMessage -import com.walletconnect.android.Core -import com.walletconnect.android.internal.common.di.AndroidCommonDITags -import com.walletconnect.android.internal.common.wcKoinApp -import com.walletconnect.android.push.notifications.PushMessagingService -import com.walletconnect.foundation.util.Logger -import com.walletconnect.sample.wallet.domain.NotificationHandler -import kotlinx.coroutines.runBlocking -import org.koin.core.qualifier.named - -@SuppressLint("MissingFirebaseInstanceTokenRefresh") -class Web3WalletMessageService : PushMessagingService() { - private val logger: Logger by lazy { wcKoinApp.koin.get(named(AndroidCommonDITags.LOGGER)) } - - override fun onMessage(message: Core.Model.Message, originalMessage: RemoteMessage) { - runBlocking { NotificationHandler.addNotification(message) } - } - - override fun newToken(token: String) { - logger.log("Registering New Token Success:\t$token") - } - - override fun registeringFailed(token: String, throwable: Throwable) { - logger.log("Registering New Token Failed:\t$token") - } - - override fun onDefaultBehavior(message: RemoteMessage) { - logger.log("onDefaultBehavior: ${message.to}") - } - - override fun onError(throwable: Throwable, defaultMessage: RemoteMessage) { - logger.error("onError Message To: ${defaultMessage.to}, throwable: $throwable") - } -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/MixPanelDelegate.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/MixPanelDelegate.kt deleted file mode 100644 index 2eafcabbc..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/domain/MixPanelDelegate.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.walletconnect.sample.wallet.domain - -import com.mixpanel.android.mpmetrics.MixpanelAPI - -lateinit var mixPanel: MixpanelAPI \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletActivity.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletActivity.kt deleted file mode 100644 index a808b3716..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletActivity.kt +++ /dev/null @@ -1,212 +0,0 @@ -@file:OptIn( - ExperimentalMaterialApi::class, - ExperimentalMaterialNavigationApi::class, - ExperimentalAnimationApi::class -) - -package com.walletconnect.sample.wallet.ui - -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.widget.Toast -import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.rememberModalBottomSheetState -import androidx.core.content.ContextCompat -import androidx.core.net.toUri -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavHostController -import com.google.accompanist.navigation.animation.rememberAnimatedNavController -import com.google.accompanist.navigation.material.BottomSheetNavigator -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.walletconnect.sample.common.ui.theme.WCSampleAppTheme -import com.walletconnect.sample.wallet.domain.NotifyDelegate -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel -import com.walletconnect.sample.wallet.ui.routes.host.WalletSampleHost -import com.walletconnect.web3.wallet.client.Web3Wallet -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import timber.log.Timber -import java.net.URLEncoder - -class Web3WalletActivity : AppCompatActivity() { - private lateinit var navController: NavHostController - private val web3walletViewModel = Web3WalletViewModel() - private val connectionsViewModel = ConnectionsViewModel() - - private val requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted: Boolean -> - if (isGranted) { - // FCM SDK (and your app) can post notifications. - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContent(web3walletViewModel, connectionsViewModel) - handleWeb3WalletEvents(web3walletViewModel, connectionsViewModel) - askNotificationPermission() - handleErrors() - handleAppLink(intent) - } - - private fun setContent( - web3walletViewModel: Web3WalletViewModel, - connectionsViewModel: ConnectionsViewModel, - ) { - setContent { - val sheetState = rememberModalBottomSheetState( - initialValue = ModalBottomSheetValue.Hidden, - skipHalfExpanded = true - ) - val bottomSheetNavigator = BottomSheetNavigator(sheetState) - val navController = rememberAnimatedNavController(bottomSheetNavigator) - this.navController = navController - val sharedPref = getPreferences(MODE_PRIVATE) - val getStartedVisited = sharedPref.getBoolean("get_started_visited", false) - WCSampleAppTheme { - WalletSampleHost( - bottomSheetNavigator, - navController, - web3walletViewModel, - connectionsViewModel, - getStartedVisited - ) - } - } - } - - private fun handleErrors() { - NotifyDelegate.notifyErrors - .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) - .onEach { error -> Timber.e(error.throwable) } - .launchIn(lifecycleScope) - } - - private fun handleWeb3WalletEvents( - web3walletViewModel: Web3WalletViewModel, - connectionsViewModel: ConnectionsViewModel, - ) { - web3walletViewModel.sessionRequestStateFlow - .onEach { - if (it.arrayOfArgs.isNotEmpty()) { - web3walletViewModel.showRequestLoader(false) - navigateWhenReady { - navController.navigate(Route.SessionRequest.path) - } - } - } - .launchIn(lifecycleScope) - - web3walletViewModel.walletEvents - .onEach { event -> - when (event) { - is SignEvent.SessionProposal -> navigateWhenReady { navController.navigate(Route.SessionProposal.path) } - is SignEvent.SessionAuthenticate -> navigateWhenReady { navController.navigate(Route.SessionAuthenticate.path) } - is SignEvent.ExpiredRequest -> { - if (navController.currentDestination?.route != Route.Connections.path) { - navController.popBackStack(route = Route.Connections.path, inclusive = false) - } - Toast.makeText(baseContext, "Request expired", Toast.LENGTH_SHORT).show() - } - - is SignEvent.Disconnect -> { - connectionsViewModel.refreshConnections() - if (navController.currentDestination?.route != Route.Connections.path) { - navController.navigate(Route.Connections.path) - } - } - - is AuthEvent.OnRequest -> navController.navigate(Route.AuthRequest.path) - else -> Unit - } - } - .launchIn(lifecycleScope) - } - - private suspend fun navigateWhenReady(navigate: () -> Unit) { - if (!::navController.isInitialized) { - delay(300) - navigate() - } else { - navigate() - } - } - - private fun handleAppLink(intent: Intent?) { - if (intent?.dataString?.contains("wc_ev") == true) { - Web3Wallet.dispatchEnvelope(intent.dataString ?: "") { - lifecycleScope.launch(Dispatchers.Main) { - Toast.makeText(this@Web3WalletActivity, "Error dispatching envelope: ${it.throwable.message}", Toast.LENGTH_SHORT).show() - } - } - } else { - when { - intent?.dataString?.startsWith("kotlin-web3wallet:/wc") == true -> { - val uri = intent.dataString?.replace("kotlin-web3wallet:/wc", "kotlin-web3wallet://wc") - intent.setData(uri?.toUri()) - } - - intent?.dataString?.startsWith("wc:") == true && intent.dataString?.contains("requestId") == false -> { - val uri = "kotlin-web3wallet://wc?uri=" + URLEncoder.encode(intent.dataString, "UTF-8") - intent.setData(uri.toUri()) - } - } - - if (intent?.dataString?.startsWith("kotlin-web3wallet://request") == true || intent?.dataString?.contains("requestId") == true) { - lifecycleScope.launch { - navigateWhenReady { - if (navController.currentDestination?.route != Route.SessionRequest.path) { - web3walletViewModel.showRequestLoader(true) - } - } - } - } - - if (intent?.dataString?.startsWith("kotlin-web3wallet://request") == false && intent.dataString?.contains("requestId") == false - ) { - lifecycleScope.launch { - navigateWhenReady { - navController.handleDeepLink(intent) - } - } - } - } - } - - override fun onNewIntent(intent: Intent?) { - super.onNewIntent(intent) - - handleAppLink(intent) - } - - private fun askNotificationPermission() { - // This is only necessary for API level >= 33 (TIRAMISU) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission( - this, - android.Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - ) { - // FCM SDK (and your app) can post notifications. - } else { - // Directly ask for the permission - requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS) - } - } - } -} diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletEvent.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletEvent.kt deleted file mode 100644 index aaf8d9c74..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletEvent.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.walletconnect.sample.wallet.ui - -sealed interface Web3WalletEvent - -object NoAction : Web3WalletEvent, NotifyEvent - -interface SignEvent : Web3WalletEvent { - object SessionProposal : SignEvent - object SessionAuthenticate : SignEvent - data class SessionRequest(val arrayOfArgs: ArrayList, val numOfArgs: Int) : SignEvent - object ExpiredRequest : SignEvent - object Disconnect : SignEvent - data class ConnectionState(val isAvailable: Boolean) : SignEvent -} - -interface AuthEvent : Web3WalletEvent { - data class OnRequest(val id: Long, val message: String) : AuthEvent -} - diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletNavGraph.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletNavGraph.kt deleted file mode 100644 index 31105e744..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/Web3WalletNavGraph.kt +++ /dev/null @@ -1,182 +0,0 @@ -@file:OptIn(ExperimentalAnimationApi::class) - -package com.walletconnect.sample.wallet.ui - -import android.annotation.SuppressLint -import androidx.compose.animation.AnimatedContentTransitionScope -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.animation.core.tween -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ModalBottomSheetDefaults -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.DialogProperties -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavDeepLink -import androidx.navigation.NavHostController -import androidx.navigation.NavType -import androidx.navigation.compose.dialog -import androidx.navigation.navArgument -import com.google.accompanist.navigation.animation.AnimatedNavHost -import com.google.accompanist.navigation.animation.composable -import com.google.accompanist.navigation.material.BottomSheetNavigator -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi -import com.google.accompanist.navigation.material.ModalBottomSheetLayout -import com.google.accompanist.navigation.material.bottomSheet -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.routes.Route -import com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.scan_uri.ScanUriRoute -import com.walletconnect.sample.wallet.ui.routes.bottomsheet_routes.update_subscription.UpdateSubscriptionRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connection_details.ConnectionDetailsRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.connections.ConnectionsViewModel -import com.walletconnect.sample.wallet.ui.routes.composable_routes.get_started.GetStartedRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.InboxRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.InboxViewModel -import com.walletconnect.sample.wallet.ui.routes.composable_routes.notifications.NotificationsScreenRoute -import com.walletconnect.sample.wallet.ui.routes.composable_routes.settings.SettingsRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.auth_request.AuthRequestRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.paste_uri.PasteUriRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_authenticate.SessionAuthenticateRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_proposal.SessionProposalRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_request.SessionRequestRoute -import com.walletconnect.sample.wallet.ui.routes.dialog_routes.snackbar_message.SnackbarMessageRoute - -@SuppressLint("RestrictedApi") -@ExperimentalMaterialNavigationApi -@Composable -fun Web3WalletNavGraph( - bottomSheetNavigator: BottomSheetNavigator, - navController: NavHostController, - web3walletViewModel: Web3WalletViewModel, - connectionsViewModel: ConnectionsViewModel, - getStartedVisited: Boolean, - modifier: Modifier = Modifier, - startDestination: String = if (getStartedVisited) Route.Connections.path else Route.GetStarted.path, -) { - var scrimColor by remember { mutableStateOf(Color.Unspecified) } - val inboxViewModel: InboxViewModel = viewModel() - - navController.addOnDestinationChangedListener( - listener = { _, destination, _ -> - if (destination.route == Route.Connections.path) { - WCDelegate.currentId = null - } - }) - - ModalBottomSheetLayout( - modifier = modifier, - bottomSheetNavigator = bottomSheetNavigator, - sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), - sheetBackgroundColor = Color.Transparent, sheetElevation = 0.dp, - scrimColor = scrimColor - ) { - val sheetState = remember { bottomSheetNavigator.navigatorSheetState } - - AnimatedNavHost( - navController = navController, - startDestination = startDestination, - enterTransition = { - slideIntoContainer( - towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, - animationSpec = tween(700) - ) - }, - exitTransition = { - slideOutOfContainer( - towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, - animationSpec = tween(700) - ) - }, - popEnterTransition = { - slideIntoContainer( - towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, - animationSpec = tween(700) - ) - }, - popExitTransition = { - slideOutOfContainer( - towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, - animationSpec = tween(700) - ) - } - ) { - composable(Route.GetStarted.path) { - GetStartedRoute(navController) - } - composable( - Route.Connections.path, - deepLinks = listOf(NavDeepLink("kotlin-web3wallet://wc")) - ) { - ConnectionsRoute(navController, connectionsViewModel, web3walletViewModel) - } - composable("${Route.ConnectionDetails.path}/{connectionId}", arguments = listOf( - navArgument("connectionId") { type = NavType.IntType } - )) { - ConnectionDetailsRoute(navController, it.arguments?.getInt("connectionId"), connectionsViewModel) - } - composable( - "${Route.Notifications.path}/{topic}", arguments = listOf( - navArgument("topic") { - type = NavType.Companion.StringType - nullable = false - }, - ) - ) { - NotificationsScreenRoute(navController, it.arguments?.getString("topic")!!, inboxViewModel) - } - composable(Route.Inbox.path) { - InboxRoute(navController, inboxViewModel) - } - composable(Route.Settings.path) { - SettingsRoute(navController) - } - bottomSheet(Route.ScanUri.path) { - web3walletViewModel.showLoader(false) - scrimColor = Color.Unspecified - ScanUriRoute(navController, sheetState, onScanSuccess = { web3walletViewModel.pair(it) }) - } - bottomSheet( - "${Route.UpdateSubscription.path}/{topic}", arguments = listOf( - navArgument("topic") { - type = NavType.Companion.StringType - nullable = false - }) - ) { - scrimColor = ModalBottomSheetDefaults.scrimColor - UpdateSubscriptionRoute(navController, sheetState, it.arguments?.getString("topic")!!) - } - dialog(Route.SessionProposal.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { - SessionProposalRoute(navController) - } - dialog(Route.AuthRequest.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { - AuthRequestRoute(navController) - } - dialog(Route.SessionAuthenticate.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { - SessionAuthenticateRoute(navController, connectionsViewModel) - } - dialog(Route.SessionRequest.path, deepLinks = listOf(NavDeepLink("kotlin-web3wallet://request")), dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { - SessionRequestRoute(navController) - } - dialog(Route.PasteUri.path, dialogProperties = DialogProperties(usePlatformDefaultWidth = false)) { - PasteUriRoute(onSubmit = { - web3walletViewModel.pair(it) - navController.popBackStack() - }) - } - bottomSheet("${Route.SnackbarMessage.path}/{message}", arguments = listOf( - navArgument("message") { type = NavType.StringType } - )) { - scrimColor = Color.Unspecified - SnackbarMessageRoute(navController, it.arguments?.getString("message")) - } - } - } -} - diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Buttons.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Buttons.kt deleted file mode 100644 index bba05f920..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/Buttons.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.walletconnect.sample.wallet.ui.common - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.walletconnect.sample.wallet.ui.common.generated.ButtonWithLoader - - -@Composable -fun Buttons( - allowButtonColor: Color, - modifier: Modifier = Modifier, - onCancel: () -> Unit = {}, - onConfirm: () -> Unit = {}, - isLoadingConfirm: Boolean, - isLoadingCancel: Boolean -) { - Row(modifier = modifier) { - Spacer(modifier = Modifier.width(18.dp)) - ButtonWithLoader( - buttonColor = Color(0xFFD6D6D6), - loaderColor = Color(0xFF000000), - modifier = Modifier - .weight(1f) - .height(46.dp) - .clickable { onCancel() }, - isLoading = isLoadingCancel, - content = { - Text( - text = "Cancel", - style = TextStyle( - fontSize = 20.0.sp, - fontWeight = FontWeight.SemiBold, - color = Color(0xFF000000), - ), - modifier = modifier.wrapContentHeight(align = Alignment.CenterVertically) - ) - } - ) - Spacer(modifier = Modifier.width(12.dp)) - ButtonWithLoader( - buttonColor = allowButtonColor, - loaderColor = Color(0xFFFFFFFF), - modifier = Modifier - .weight(1f) - .height(46.dp) - .clickable { onConfirm() }, - isLoadingConfirm, - content = { - Text( - text = "Confirm", - style = TextStyle( - fontSize = 20.0.sp, - fontWeight = FontWeight.SemiBold, - color = Color( - alpha = 255, - red = 255, - green = 255, - blue = 255 - ), - ), - modifier = modifier.wrapContentHeight(align = Alignment.CenterVertically) - ) - } - ) - Spacer(modifier = Modifier.width(20.dp)) - } -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/ImageUrl.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/ImageUrl.kt deleted file mode 100644 index 3f1594d41..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/common/ImageUrl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.walletconnect.sample.wallet.ui.common - -data class ImageUrl( - val small: String, - val medium: String, - val large: String, -) - -fun List.toImageUrl(): ImageUrl { - return ImageUrl( - small = this.getOrElse(0) { "" }, - medium = this.getOrElse(1) { "" }, - large = this.getOrElse(2) { "" }, - ) -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/Route.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/Route.kt deleted file mode 100644 index 4a7d883f5..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/Route.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes - -import androidx.navigation.NavController - -sealed class Route(val path: String) { - object GetStarted : Route("get_started") - object Connections : Route("connections") - object SessionProposal : Route("session_proposal") - object SessionRequest : Route("session_request") - object AuthRequest : Route("auth_request") - object SessionAuthenticate : Route("session_authenticate") - object PasteUri : Route("paste_uri") - object ScanUri : Route("scan_uri") - - object ConnectionDetails : Route("connection_details") - object SnackbarMessage : Route("snackbar_message") - object ExploreDapps : Route("explore_dapps") - object Inbox : Route("inbox") - object Notifications : Route("notifications") - object UpdateSubscription : Route("update_subscription") - object Settings : Route("settings") -} - -fun NavController.showSnackbar(message: String) { - navigate("${Route.SnackbarMessage.path}/$message") -} - - - - diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt deleted file mode 100644 index 5e1d8accf..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/connections/ConnectionUI.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.connections - -import com.walletconnect.web3.wallet.client.Wallet - -data class ConnectionUI( - val id: Int, - val type: ConnectionType, - val name: String, - val uri: String, - val icon: String?, -) - -sealed class ConnectionType { - data class Sign(val topic: String, val namespaces: Map) : ConnectionType() -} - diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt deleted file mode 100644 index 2528ba93f..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/composable_routes/inbox/subscriptions/SubscriptionsTab.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.subscriptions - - -import androidx.compose.runtime.Composable -import androidx.navigation.NavHostController -import com.walletconnect.sample.wallet.ui.common.subscriptions.ActiveSubscriptionsUI -import com.walletconnect.sample.wallet.ui.routes.composable_routes.inbox.SubscriptionsState - - -@Composable -fun SubscriptionsTab( - navController: NavHostController, - state: SubscriptionsState, - activeSubscriptions: List, - onDiscoverMoreClicked: () -> Unit, -) { - when (state) { - is SubscriptionsState.Failure -> { - - } - - is SubscriptionsState.Unsubscribed -> { - NoActiveSubscriptions(onDiscoverMoreClicked) - } - - is SubscriptionsState.Searching -> { - - } - - SubscriptionsState.Success -> { - ActiveSubscriptions(navController, activeSubscriptions) - } - } -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestRoute.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestRoute.kt deleted file mode 100644 index 71c097244..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestRoute.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.auth_request - -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController -import com.walletconnect.sample.common.ui.themedColor -import com.walletconnect.sample.wallet.ui.common.Buttons -import com.walletconnect.sample.wallet.ui.common.Content -import com.walletconnect.sample.wallet.ui.common.InnerContent -import com.walletconnect.sample.wallet.ui.common.SemiTransparentDialog -import com.walletconnect.sample.wallet.ui.common.peer.Peer -import com.walletconnect.sample.wallet.ui.common.peer.getValidationColor -import com.walletconnect.sample.wallet.ui.routes.showSnackbar -import kotlinx.coroutines.launch - -@Composable -fun AuthRequestRoute(navController: NavHostController, authRequestViewModel: AuthRequestViewModel = viewModel()) { - val authRequestUI = authRequestViewModel.authRequest ?: throw Exception("Missing auth request") - val composableScope = rememberCoroutineScope() - val allowButtonColor = getValidationColor(authRequestUI.peerContextUI.validation) - var isConfirmLoading by remember { mutableStateOf(false) } - var isCancelLoading by remember { mutableStateOf(false) } - - SemiTransparentDialog { - Spacer(modifier = Modifier.height(24.dp)) - Peer(peerUI = authRequestUI.peerUI, "would like to connect", authRequestUI.peerContextUI) - Spacer(modifier = Modifier.height(16.dp)) - Message(authRequestUI = authRequestUI) - Spacer(modifier = Modifier.height(16.dp)) - Buttons(allowButtonColor, onCancel = { - isCancelLoading = true - composableScope.launch { - try { - authRequestViewModel.reject() - navController.popBackStack() - navController.showSnackbar("Auth Request declined") - } catch (e: Throwable) { - closeAndShowError(navController, e.message) - } - } - }, onConfirm = { - isConfirmLoading = true - composableScope.launch { - try { - authRequestViewModel.approve() - navController.popBackStack() - navController.showSnackbar("Auth Request approved") - } catch (e: Exception) { - closeAndShowError(navController, e.message) - } - } - }, - isLoadingConfirm = isConfirmLoading, - isLoadingCancel = isCancelLoading - ) - Spacer(modifier = Modifier.height(16.dp)) - } -} - -private fun closeAndShowError(navController: NavHostController, message: String?) { - navController.popBackStack() - navController.showSnackbar(message ?: "Auth request error, please check your Internet connection") -} - -@Composable -fun Message(authRequestUI: AuthRequestUI) { - Content(title = "Message") { - InnerContent { - Text( - modifier = Modifier.padding(vertical = 10.dp, horizontal = 13.dp), - text = authRequestUI.message, style = TextStyle(fontWeight = FontWeight.Medium, fontSize = 13.sp, color = themedColor(darkColor = Color(0xFF9ea9a9), lightColor = Color(0xFF788686))) - ) - } - } -} - diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestStore.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestStore.kt deleted file mode 100644 index e66c17c36..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestStore.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.auth_request - -import com.walletconnect.web3.wallet.client.Wallet -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow - -//todo: remove after implementing pending request -object AuthRequestStore { - private var _activeSessions: MutableStateFlow> = MutableStateFlow(emptyList()) - var activeSessions: StateFlow> = _activeSessions.asStateFlow() - - fun addActiveSession(authRequest: Wallet.Model.AuthRequest) { - val updatedList = _activeSessions.value.toMutableList() - updatedList.add(authRequest) - _activeSessions.value = updatedList - } - - fun removeActiveSession(authRequest: Wallet.Model.AuthRequest) { - val updatedList = _activeSessions.value.toMutableList() - updatedList.remove(authRequest) - _activeSessions.value = updatedList - } - - - fun removeActiveSession(id: Long) { - val updatedList = _activeSessions.value.toMutableList() - updatedList.removeAt(updatedList.indexOfFirst { it.id == id }) - _activeSessions.value = updatedList - } -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestUI.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestUI.kt deleted file mode 100644 index 7eb88c914..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestUI.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.auth_request - -import com.walletconnect.sample.wallet.ui.common.peer.PeerContextUI -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI - -data class AuthRequestUI( - val peerUI: PeerUI, - val message: String, - val peerContextUI: PeerContextUI, -) \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestViewModel.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestViewModel.kt deleted file mode 100644 index 4f2ff31c4..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/auth_request/AuthRequestViewModel.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.auth_request - -import androidx.lifecycle.ViewModel -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase -import com.walletconnect.android.utils.cacao.signHex -import com.walletconnect.sample.wallet.domain.ISSUER -import com.walletconnect.sample.wallet.domain.PRIVATE_KEY_1 -import com.walletconnect.sample.wallet.domain.WCDelegate -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI -import com.walletconnect.sample.wallet.ui.common.peer.toPeerUI -import com.walletconnect.web3.wallet.client.Wallet -import com.walletconnect.web3.wallet.client.Web3Wallet -import com.walletconnect.web3.wallet.utils.CacaoSigner -import com.walletconnect.web3.wallet.utils.SignatureType -import org.web3j.utils.Numeric.toHexString -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine - -class AuthRequestViewModel : ViewModel() { - val authRequest: AuthRequestUI? - get() = generateAuthRequestUI() - - suspend fun approve() { - return suspendCoroutine { continuation -> - if (WCDelegate.authRequestEvent != null) { - val request = requireNotNull(WCDelegate.authRequestEvent!!.first) - val message = Web3Wallet.formatMessage(Wallet.Params.FormatMessage(request.payloadParams, ISSUER)) ?: throw Exception("Error formatting message") - Web3Wallet.respondAuthRequest( - Wallet.Params.AuthRequestResponse.Result( - id = request.id, - signature = CacaoSigner.signHex(toHexString(message.toByteArray()), PRIVATE_KEY_1, SignatureType.EIP191), - issuer = ISSUER - ), - onSuccess = { - WCDelegate.authRequestEvent = null - AuthRequestStore.addActiveSession(request) - continuation.resume(Unit) - }, - onError = { error -> - Firebase.crashlytics.recordException(error.throwable) - AuthRequestStore.removeActiveSession(request) - WCDelegate.authRequestEvent = null - continuation.resumeWithException(error.throwable) - }) - } - } - } - - suspend fun reject() { - return suspendCoroutine { continuation -> - if (WCDelegate.authRequestEvent != null) { - val request = requireNotNull(WCDelegate.authRequestEvent!!.first) - //todo: Define Error Codes - Web3Wallet.respondAuthRequest( - Wallet.Params.AuthRequestResponse.Error( - request.id, 12001, "User Rejected Request" - ), - onSuccess = { - WCDelegate.authRequestEvent = null - continuation.resume(Unit) - }, - onError = { error -> - Firebase.crashlytics.recordException(error.throwable) - WCDelegate.authRequestEvent = null - continuation.resumeWithException(error.throwable) - }) - } - } - } - - private fun generateAuthRequestUI(): AuthRequestUI? { - return if (WCDelegate.authRequestEvent != null) { - val (authRequest, authContext) = WCDelegate.authRequestEvent!! - val message = Web3Wallet.formatMessage(Wallet.Params.FormatMessage(authRequest.payloadParams, ISSUER)) ?: throw Exception("Error formatting message") - - AuthRequestUI( - peerUI = PeerUI( - peerIcon = "https://raw.githubusercontent.com/WalletConnect/walletconnect-assets/master/Icon/Gradient/Icon.png", - peerName = "WalletConnect", - peerUri = "https://walletconnect.com/", - peerDescription = "The communications protocol for web3.", - ), - message = message, - peerContextUI = authContext.toPeerUI() - ) - } else null - } -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt deleted file mode 100644 index 80fdc5aa7..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/routes/dialog_routes/session_request/SessionRequestUI.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.walletconnect.sample.wallet.ui.routes.dialog_routes.session_request - -import com.walletconnect.sample.wallet.ui.common.peer.PeerContextUI -import com.walletconnect.sample.wallet.ui.common.peer.PeerUI - -sealed class SessionRequestUI { - object Initial : SessionRequestUI() - - data class Content( - val peerUI: PeerUI, - val topic: String, - val requestId: Long, - val param: String, - val chain: String?, - val method: String, - val peerContextUI: PeerContextUI - ) : SessionRequestUI() -} \ No newline at end of file diff --git a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/ConnectionState.kt b/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/ConnectionState.kt deleted file mode 100644 index 93b5fb1a2..000000000 --- a/sample/wallet/src/main/kotlin/com/walletconnect/sample/wallet/ui/state/ConnectionState.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.walletconnect.sample.wallet.ui.state - -import kotlinx.coroutines.flow.MutableStateFlow - -val connectionStateFlow: MutableStateFlow = MutableStateFlow(ConnectionState.Idle) - -sealed class ConnectionState() { - data class Error(val message: String) : ConnectionState() - object Ok : ConnectionState() - object Idle : ConnectionState() -} \ No newline at end of file diff --git a/sample/wallet/src/main/res/navigation/nav_graph.xml b/sample/wallet/src/main/res/navigation/nav_graph.xml index e458f9485..6dc81f973 100644 --- a/sample/wallet/src/main/res/navigation/nav_graph.xml +++ b/sample/wallet/src/main/res/navigation/nav_graph.xml @@ -6,19 +6,11 @@ - - + android:label="WalletKitActivity" /> - - \ No newline at end of file diff --git a/sample/wallet/src/test/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegateTest.kt b/sample/wallet/src/test/kotlin/com/reown/sample/wallet/domain/EthAccountDelegateTest.kt similarity index 79% rename from sample/wallet/src/test/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegateTest.kt rename to sample/wallet/src/test/kotlin/com/reown/sample/wallet/domain/EthAccountDelegateTest.kt index 7f3b07723..d82e12e58 100644 --- a/sample/wallet/src/test/kotlin/com/walletconnect/sample/wallet/domain/EthAccountDelegateTest.kt +++ b/sample/wallet/src/test/kotlin/com/reown/sample/wallet/domain/EthAccountDelegateTest.kt @@ -1,11 +1,11 @@ -package com.walletconnect.sample.wallet.domain - -import com.walletconnect.android.cacao.signature.SignatureType -import com.walletconnect.android.internal.common.model.ProjectId -import com.walletconnect.android.internal.common.signing.message.MessageSignatureVerifier -import com.walletconnect.android.utils.cacao.sign -import com.walletconnect.util.hexToBytes -import com.walletconnect.web3.wallet.utils.CacaoSigner +package com.reown.sample.wallet.domain + +import com.reown.android.cacao.signature.SignatureType +import com.reown.android.internal.common.model.ProjectId +import com.reown.android.internal.common.signing.message.MessageSignatureVerifier +import com.reown.android.utils.cacao.sign +import com.reown.util.hexToBytes +import com.reown.walletkit.utils.CacaoSigner import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue diff --git a/settings.gradle.kts b/settings.gradle.kts index 3358c9dc1..bd1fa7f75 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,4 @@ -rootProject.name = "WalletConnect Kotlin" +rootProject.name = "Reown Kotlin" val excludedDirs = listOf( "undefined",