From 88d1633e063905d78307f08406b52b5928326f63 Mon Sep 17 00:00:00 2001 From: Thomas Bereczky Date: Sun, 19 Nov 2023 14:57:37 +0100 Subject: [PATCH 01/39] Fastlane deploy test --- .github/workflows/nightly.yml | 131 +++++++++++++++++++++++++++ .tool-versions | 4 +- apps/wallet-mobile/.tool-versions | 4 +- apps/wallet-mobile/fastlane/Fastfile | 36 +++++++- 4 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 0000000000..76f72d1c99 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,131 @@ +on: + push: + branches: + - "release/**" + +jobs: + build: + runs-on: macos-13 + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.0' + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + - name: Install Android Tools + run: | + which sdkmanager + sdkmanager "tools" + sdkmanager --update + sdkmanager "build-tools;33.0.0" "platform-tools" "platforms;android-33" "tools" + echo "y" | sdkmanager --licenses + sdkmanager --install "ndk;23.1.7779620" + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: ruby/setup-ruby@v1 + with: + working-directory: 'apps/wallet-mobile' + ruby-version: '3.1.0' + bundler-cache: true + + - uses: maierj/fastlane-action@v3.0.0 + with: + subdirectory: 'apps/wallet-mobile' + lane: 'bump_build_android' + + - uses: maierj/fastlane-action@v3.0.0 + with: + subdirectory: 'apps/wallet-mobile' + lane: 'bump_build_ios' + + + - name: Commit changes + uses: EndBug/add-and-commit@v9 + with: + author_name: ci-bot + author_email: support@emurgo.io + message: 'chore(release): nightly' + add: '.' + + - name: Install deps for ASDF + run: brew install coreutils curl git + + - name: Install ASDF + run: brew install asdf + + - name: Install ASDF packages + run: | + echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> $HOME/.zshrc + echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> $HOME/.bashrc + . /usr/local/opt/asdf/libexec/asdf.sh + cd apps/wallet-mobile + asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git + asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git + asdf plugin-add java https://github.com/halcyon/asdf-java.git + asdf plugin-add rust https://github.com/code-lever/asdf-rust.git + asdf plugin-add python + asdf install + asdf install python 2.7.13 + rustup default 1.69 + rustup target add aarch64-apple-darwin aarch64-apple-ios aarch64-apple-ios-sim aarch64-linux-android armv7-linux-androideabi i686-linux-android wasm32-unknown-unknown x86_64-apple-ios x86_64-linux-android + bash -c -l "rustup default 1.69" + bash -l -c "rustup target add aarch64-apple-darwin aarch64-apple-ios aarch64-apple-ios-sim aarch64-linux-android armv7-linux-androideabi i686-linux-android wasm32-unknown-unknown x86_64-apple-ios x86_64-linux-android" + gem install cocoapods fastlane + ruby --version + python --version + node --version + rustc --version + java --version + cargo install --version 3.1.1 cargo-lipo + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Yarn & Pod Install + run: | + . /usr/local/opt/asdf/libexec/asdf.sh + yarn install + cd apps/wallet-mobile + yarn install + cd ios + pod install + cd .. + yarn workspaces run build + - name: Copy Apple Signing Key + run: | + mkdir -p $HOME/.yoroi/ios + echo ${{secrets.APP_STORE_KEY}} | base64 -d > $HOME/.yoroi/ios/AuthKey_PH9Z89M567.p8 + chmod 600 $HOME/.yoroi/ios/AuthKey_PH9Z89M567.p8 + + - name: Copy Android Keys + run: | + mkdir -p $HOME/.yoroi/android + echo ${{secrets.ANDROID_NIGHTLY_KEYSTORE}} | base64 -d > $HOME/.yoroi/android/nightly.keystore + chmod 600 $HOME/.yoroi/android/nightly.keystore + echo ${{secrets.ANDROID_KEYSTORE_PASS}} > $HOME/.yoroi/android/nightly.pass + echo ${{secrets.ANDROID_SERVICE_ACCOUNT_JSON}} | base64 -d > $HOME/.yoroi/android/service-account.json + + - name: Copy Yoroi Certs SSH Key + run: | + mkdir -p $HOME/.ssh + echo ${{secrets.CERT_SSH_KEY}} | base64 -d > $HOME/.ssh/id_rsa + chmod 600 $HOME/.ssh/id_rsa + + - name: Fastlane Deploy iOS and Android Nightly + run: | + . /usr/local/opt/asdf/libexec/asdf.sh + cd apps/wallet-mobile + export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk + export ANDROID_HOME=$HOME/Library/Android/sdk + export JAVA_HOME=$(/usr/libexec/java_home -v 11) + export PATH=$JAVA_HOME/bin:$PATH + export ANDROID_KEYSTORE_PASS="${{secrets.ANDROID_KEYSTORE_PASS}}" + export ANDROID_KEY_PASS="${{secrets.ANDROID_KEY_PASS}}" + export CI_KEYCHAIN_NAME="${{secrets.CI_KEYCHAIN_NAME}}" + export CI_KEYCHAIN_PASSWORD="${{secrets.CI_KEYCHAIN_PASSWORD}}" + export FASTLANE_PASSWORD="${{secrets.FASTLANE_PASSWORD}}" + export MATCH_PASSWORD="${{secrets.MATCH_PASSWORD}}" + fastlane ios release --env nightly + asdf global python 2.7.13 + fastlane android release --env nightly + diff --git a/.tool-versions b/.tool-versions index 909950232f..d698c578f7 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ nodejs 16.19.0 rust 1.69.0 java adoptopenjdk-11.0.18+10 -ruby 2.7.6 -python 3.11.3 +ruby 3.2.2 +python 3.11.3 \ No newline at end of file diff --git a/apps/wallet-mobile/.tool-versions b/apps/wallet-mobile/.tool-versions index 909950232f..d698c578f7 100644 --- a/apps/wallet-mobile/.tool-versions +++ b/apps/wallet-mobile/.tool-versions @@ -1,5 +1,5 @@ nodejs 16.19.0 rust 1.69.0 java adoptopenjdk-11.0.18+10 -ruby 2.7.6 -python 3.11.3 +ruby 3.2.2 +python 3.11.3 \ No newline at end of file diff --git a/apps/wallet-mobile/fastlane/Fastfile b/apps/wallet-mobile/fastlane/Fastfile index 3ebd749352..474d3714fb 100644 --- a/apps/wallet-mobile/fastlane/Fastfile +++ b/apps/wallet-mobile/fastlane/Fastfile @@ -23,7 +23,14 @@ platform :ios do environment = lane_context[SharedValues::ENVIRONMENT] scheme = environment == "nightly" ? "nightly" : "yoroi" - match(app_identifier: ENV["APP_STORE_BUNDLE_ID"], type: "appstore") + create_keychain( + name: ENV["CI_KEYCHAIN_NAME"], + password: ENV["CI_KEYCHAIN_PASSWORD"], + default_keychain: true, + unlock: true, + timeout: 3600, + lock_when_sleeps: false + ) api_key = app_store_connect_api_key( key_id: ENV["APP_STORE_KEY_ID"], @@ -32,7 +39,20 @@ platform :ios do duration: 1200, in_house: false ) - + + match(app_identifier: ENV["APP_STORE_BUNDLE_ID"], type: "appstore", api_key: api_key, keychain_name: ENV["CI_KEYCHAIN_NAME"], keychain_password: ENV["CI_KEYCHAIN_PASSWORD"]) + + set_version_ios + bump_build_ios + + update_code_signing_settings( + use_automatic_signing: false, + path: XC_PROJECT, + code_sign_identity: "iPhone Distribution", + bundle_identifier: ENV["APP_STORE_BUNDLE_ID"], + profile_name: "match AppStore #{ENV["APP_STORE_BUNDLE_ID"]}" + ) + begin gym( scheme: scheme, @@ -44,18 +64,23 @@ platform :ios do xcargs: `-UseNewBuildSystem=YES`, output_directory: "./ios", output_name: "yoroi.ipa", + sdk: "iphoneos", export_options: { method: "app-store", - signingStyle: "automatic" + signingStyle: "manual", + provisioningProfiles: { + ENV["APP_STORE_BUNDLE_ID"] => "match AppStore #{ENV["APP_STORE_BUNDLE_ID"]}" + } }, ) rescue => e - xcode_log_path = '~/Library/Logs/gym/yoroi-yoroi.log' + xcode_log_path = "~/Library/Logs/gym/#{scheme}-#{scheme}.log" p "iOS build failed -> #{xcode_log_path}" sh "cat #{xcode_log_path}" raise 'iOS build failed.' end + enable_automatic_code_signing(path: XC_PROJECT) if ENV['DRY_RUN'] == 'true' UI.message("DRY RUN: Skipping upload to App Store") @@ -77,6 +102,9 @@ platform :android do gradle(task: "clean", project_dir: 'android/') + bump_build_android + set_version_android + gradle( task: "assemble" + build_type, build_type: "Release", From 10d0d6bb01c115b77435d21d0d14f83a0bd89dfc Mon Sep 17 00:00:00 2001 From: ci-bot Date: Sun, 19 Nov 2023 15:20:29 +0000 Subject: [PATCH 02/39] chore(release): nightly --- apps/wallet-mobile/.bundle/config | 7 +++++-- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/wallet-mobile/.bundle/config b/apps/wallet-mobile/.bundle/config index 848943bb52..8d515c4d18 100644 --- a/apps/wallet-mobile/.bundle/config +++ b/apps/wallet-mobile/.bundle/config @@ -1,2 +1,5 @@ -BUNDLE_PATH: "vendor/bundle" -BUNDLE_FORCE_RUBY_PLATFORM: 1 +--- +BUNDLE_PATH: "/Users/runner/work/yoroi/yoroi/apps/wallet-mobile/vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: "1" +BUNDLE_DEPLOYMENT: "true" +BUNDLE_JOBS: "4" diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index 7eab9413e4..dd9b7ffa60 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 540 + versionCode 541 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index 90ae1c47ca..7d5f34bdec 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 483 + 484 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index 0fd933bb0b..fe78703fb6 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 483; + CURRENT_PROJECT_VERSION = 484; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 483; + CURRENT_PROJECT_VERSION = 484; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 483; + CURRENT_PROJECT_VERSION = 484; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 483; + CURRENT_PROJECT_VERSION = 484; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index 1a1f808867..c867aa0bfa 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 483 + 484 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index 6735cbab58..796e0d8a46 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 483 + 484 From 0425e186941958559f202b803e60d3cbc8a1db0d Mon Sep 17 00:00:00 2001 From: CryptoTitan Date: Sun, 19 Nov 2023 21:53:16 +0100 Subject: [PATCH 03/39] Update nightly.yml Signed-off-by: CryptoTitan --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 76f72d1c99..e6196b30a2 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -5,6 +5,7 @@ on: jobs: build: + runs-on: macos-13 steps: - uses: maxim-lobanov/setup-xcode@v1 From c4e254070f0b2a67eb02369dbdce1a44bef2551b Mon Sep 17 00:00:00 2001 From: ci-bot Date: Sun, 19 Nov 2023 20:56:18 +0000 Subject: [PATCH 04/39] chore(release): nightly --- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index dd9b7ffa60..cb00ae9fb4 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 541 + versionCode 542 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index 7d5f34bdec..469fa3c6c0 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 484 + 485 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index fe78703fb6..c58fcf2fd1 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 484; + CURRENT_PROJECT_VERSION = 485; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 484; + CURRENT_PROJECT_VERSION = 485; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 484; + CURRENT_PROJECT_VERSION = 485; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 484; + CURRENT_PROJECT_VERSION = 485; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index c867aa0bfa..033418128a 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 484 + 485 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index 796e0d8a46..939d323f97 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 484 + 485 From 5c5d1b7dbe7c44c9037427489c1fdadcfa851918 Mon Sep 17 00:00:00 2001 From: CryptoTitan Date: Sun, 19 Nov 2023 22:33:44 +0100 Subject: [PATCH 05/39] Update nightly.yml Signed-off-by: CryptoTitan --- .github/workflows/nightly.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e6196b30a2..76f72d1c99 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -5,7 +5,6 @@ on: jobs: build: - runs-on: macos-13 steps: - uses: maxim-lobanov/setup-xcode@v1 From 901d6f62e08c4f00c3f0260b37e6b76a29473840 Mon Sep 17 00:00:00 2001 From: ci-bot Date: Sun, 19 Nov 2023 21:35:17 +0000 Subject: [PATCH 06/39] chore(release): nightly --- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index cb00ae9fb4..8487cbbbce 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 542 + versionCode 543 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index 469fa3c6c0..33eea0348a 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 485 + 486 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index c58fcf2fd1..99f24739f3 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 485; + CURRENT_PROJECT_VERSION = 486; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 485; + CURRENT_PROJECT_VERSION = 486; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 485; + CURRENT_PROJECT_VERSION = 486; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 485; + CURRENT_PROJECT_VERSION = 486; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index 033418128a..2e14e91fc5 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 485 + 486 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index 939d323f97..07dc2bfadd 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 485 + 486 From 3dd8941c5eecf37d4e5e8b5d89ccacdb6d3afe1c Mon Sep 17 00:00:00 2001 From: CryptoTitan Date: Mon, 20 Nov 2023 00:14:03 +0100 Subject: [PATCH 07/39] Update build.gradle Signed-off-by: CryptoTitan --- apps/wallet-mobile/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index 8487cbbbce..953d6ea1db 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 543 + versionCode 538 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' From a7d89943f4014b95842c4457bf9ecb4f73a1f8ca Mon Sep 17 00:00:00 2001 From: ci-bot Date: Sun, 19 Nov 2023 23:16:13 +0000 Subject: [PATCH 08/39] chore(release): nightly --- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index 953d6ea1db..bfe77362bc 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 538 + versionCode 539 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index 33eea0348a..ab13f9a948 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 486 + 487 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index 99f24739f3..42d867efba 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 486; + CURRENT_PROJECT_VERSION = 487; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 486; + CURRENT_PROJECT_VERSION = 487; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 486; + CURRENT_PROJECT_VERSION = 487; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 486; + CURRENT_PROJECT_VERSION = 487; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index 2e14e91fc5..345875662d 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 486 + 487 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index 07dc2bfadd..0bcc9510eb 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 486 + 487 From 8d4abd6f4764dca2d4b82bb88b4c15fa388f83ea Mon Sep 17 00:00:00 2001 From: CryptoTitan Date: Mon, 20 Nov 2023 01:30:04 +0100 Subject: [PATCH 09/39] Update build.gradle Signed-off-by: CryptoTitan --- apps/wallet-mobile/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index bfe77362bc..7eab9413e4 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 539 + versionCode 540 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' From 09cc21b769ebdcd5596998539b6d3c2c91bc3f1e Mon Sep 17 00:00:00 2001 From: ci-bot Date: Mon, 20 Nov 2023 00:32:19 +0000 Subject: [PATCH 10/39] chore(release): nightly --- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index 7eab9413e4..dd9b7ffa60 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 540 + versionCode 541 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index ab13f9a948..833baedab6 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 487 + 488 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index 42d867efba..d3a341b978 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 487; + CURRENT_PROJECT_VERSION = 488; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 487; + CURRENT_PROJECT_VERSION = 488; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 487; + CURRENT_PROJECT_VERSION = 488; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 487; + CURRENT_PROJECT_VERSION = 488; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index 345875662d..f99a7fcf27 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 487 + 488 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index 0bcc9510eb..c1a8c9da37 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 487 + 488 From a12a677bfc5a6787d8d23c7c6f2c0b45f8903427 Mon Sep 17 00:00:00 2001 From: CryptoTitan Date: Mon, 20 Nov 2023 10:27:15 +0100 Subject: [PATCH 11/39] Update build.gradle Signed-off-by: CryptoTitan --- apps/wallet-mobile/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index dd9b7ffa60..b9c20f28a4 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 541 + versionCode 600 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' From 84c78d3bb301ccfd30cd7674962e362ce7bd1e78 Mon Sep 17 00:00:00 2001 From: ci-bot Date: Mon, 20 Nov 2023 09:28:38 +0000 Subject: [PATCH 12/39] chore(release): nightly --- apps/wallet-mobile/android/app/build.gradle | 2 +- apps/wallet-mobile/ios/nightly.plist | 2 +- apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj | 8 ++++---- apps/wallet-mobile/ios/yoroi/Info.plist | 2 +- apps/wallet-mobile/ios/yoroiTests/Info.plist | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/wallet-mobile/android/app/build.gradle b/apps/wallet-mobile/android/app/build.gradle index b9c20f28a4..7768787e89 100644 --- a/apps/wallet-mobile/android/app/build.gradle +++ b/apps/wallet-mobile/android/app/build.gradle @@ -108,7 +108,7 @@ android { applicationId "com.emurgo" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 600 + versionCode 601 versionName "4.23.0" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/wallet-mobile/ios/nightly.plist b/apps/wallet-mobile/ios/nightly.plist index 833baedab6..a6389a39bf 100644 --- a/apps/wallet-mobile/ios/nightly.plist +++ b/apps/wallet-mobile/ios/nightly.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 488 + 489 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj index d3a341b978..0af1c9edf9 100644 --- a/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj +++ b/apps/wallet-mobile/ios/yoroi.xcodeproj/project.pbxproj @@ -818,7 +818,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 489; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env"; @@ -857,7 +857,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 489; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.production"; INFOPLIST_FILE = yoroi/Info.plist; @@ -1046,7 +1046,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 489; DEVELOPMENT_TEAM = F8NVT2G2L4; ENABLE_BITCODE = NO; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; @@ -1085,7 +1085,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-nightly"; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 489; DEVELOPMENT_TEAM = F8NVT2G2L4; ENVFILE = "$(PODS_ROOT)/../../.env.nightly"; INFOPLIST_FILE = nightly.plist; diff --git a/apps/wallet-mobile/ios/yoroi/Info.plist b/apps/wallet-mobile/ios/yoroi/Info.plist index f99a7fcf27..79ea44ec55 100644 --- a/apps/wallet-mobile/ios/yoroi/Info.plist +++ b/apps/wallet-mobile/ios/yoroi/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 488 + 489 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/apps/wallet-mobile/ios/yoroiTests/Info.plist b/apps/wallet-mobile/ios/yoroiTests/Info.plist index c1a8c9da37..43dee9f0cf 100644 --- a/apps/wallet-mobile/ios/yoroiTests/Info.plist +++ b/apps/wallet-mobile/ios/yoroiTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 488 + 489 From 7173f80f652169d931f4a882aefd71139e530bd7 Mon Sep 17 00:00:00 2001 From: banklesss <105349292+banklesss@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:16:58 +0100 Subject: [PATCH 13/39] chore: upgrade yarn (#2910) --- packages/api/package.json | 2 +- packages/banxa/package.json | 2 +- packages/common/package.json | 2 +- packages/swap/package.json | 2 +- packages/types/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index 26fb8c4da6..1978735d83 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -174,7 +174,7 @@ "react": ">= 16.8.0 <= 19.0.0", "react-query": "^3.39.3" }, - "packageManager": "^yarn@1.22.15", + "packageManager": "yarn@1.22.21", "engines": { "node": ">= 16.19.0" }, diff --git a/packages/banxa/package.json b/packages/banxa/package.json index 36640fae9b..d1d31b5dc4 100644 --- a/packages/banxa/package.json +++ b/packages/banxa/package.json @@ -170,7 +170,7 @@ "optionalDependencies": { "@react-native-async-storage/async-storage": "^1.18.1" }, - "packageManager": "^yarn@1.22.15", + "packageManager": "yarn@1.22.21", "engines": { "node": ">= 16.19.0" }, diff --git a/packages/common/package.json b/packages/common/package.json index e2733cbb8b..c9926887a2 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -173,7 +173,7 @@ "react": ">= 16.8.0 <= 19.0.0", "react-query": "^3.39.3" }, - "packageManager": "^yarn@1.22.15", + "packageManager": "yarn@1.22.21", "engines": { "node": ">= 16.19.0" }, diff --git a/packages/swap/package.json b/packages/swap/package.json index 304e8f5dfa..6d1dfa7813 100644 --- a/packages/swap/package.json +++ b/packages/swap/package.json @@ -177,7 +177,7 @@ "optionalDependencies": { "@react-native-async-storage/async-storage": "^1.19.3" }, - "packageManager": "^yarn@1.22.15", + "packageManager": "yarn@1.22.21", "engines": { "node": ">= 16.19.0" }, diff --git a/packages/types/package.json b/packages/types/package.json index 2601df2917..efc7989d24 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -110,7 +110,7 @@ "release-it": "^15.0.0", "typescript": "^4.5.2" }, - "packageManager": "^yarn@1.22.15", + "packageManager": "yarn@1.22.21", "engines": { "node": ">= 16.19.0" }, From 3c34a1d444d624917638ebe600409abf66ee34c3 Mon Sep 17 00:00:00 2001 From: Michal Date: Tue, 21 Nov 2023 10:43:33 +0000 Subject: [PATCH 14/39] fix(swap): Fix order submitted and order cancelled event tracking (#2912) --- .../ConfirmTxScreen/ConfirmTxScreen.tsx | 47 ++++++++++--------- .../StartSwapScreen/ListOrders/OpenOrders.tsx | 5 +- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx b/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx index 59df280370..f6917c90c7 100644 --- a/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx +++ b/apps/wallet-mobile/src/features/Swap/useCases/ConfirmTxScreen/ConfirmTxScreen.tsx @@ -48,6 +48,22 @@ export const ConfirmTxScreen = () => { {onSuccess: (rootKey) => signAndSubmitTx({unsignedTx, rootKey})}, ) + const trackSwapOrderSubmitted = () => { + if (orderData.selectedPoolCalculation === undefined) return + track.swapOrderSubmitted({ + from_asset: [ + {asset_name: sellTokenInfo.name, asset_ticker: sellTokenInfo.ticker, policy_id: sellTokenInfo.group}, + ], + to_asset: [{asset_name: buyTokenInfo.name, asset_ticker: buyTokenInfo.ticker, policy_id: buyTokenInfo.group}], + order_type: orderData.type, + slippage_tolerance: orderData.slippage, + from_amount: orderData.amounts.sell.quantity, + to_amount: orderData.amounts.buy.quantity, + pool_source: orderData.selectedPoolCalculation.pool.provider, + swap_fees: Number(orderData.selectedPoolCalculation.cost.batcherFee), + }) + } + const {signAndSubmitTx, isLoading: processingTx} = useSignAndSubmitTx( {wallet}, { @@ -59,21 +75,7 @@ export const ConfirmTxScreen = () => { }, submitTx: { onSuccess: () => { - if (orderData.selectedPoolCalculation === undefined) return - track.swapOrderSubmitted({ - from_asset: [ - {asset_name: sellTokenInfo.name, asset_ticker: sellTokenInfo.ticker, policy_id: sellTokenInfo.group}, - ], - to_asset: [ - {asset_name: buyTokenInfo.name, asset_ticker: buyTokenInfo.ticker, policy_id: buyTokenInfo.group}, - ], - order_type: orderData.type, - slippage_tolerance: orderData.slippage, - from_amount: orderData.amounts.sell.quantity, - to_amount: orderData.amounts.buy.quantity, - pool_source: orderData.selectedPoolCalculation.pool.provider, - swap_fees: Number(orderData.selectedPoolCalculation.cost.batcherFee), - }) + trackSwapOrderSubmitted() navigate.submittedTx(signedTxRef.current?.signedTx.id ?? '') }, onError: () => { @@ -84,6 +86,14 @@ export const ConfirmTxScreen = () => { }, ) + const onPasswordTxSuccess = (signedTx: YoroiSignedTx) => { + trackSwapOrderSubmitted() + closeModal() + InteractionManager.runAfterInteractions(() => { + navigate.submittedTx(signedTx.signedTx.id) + }) + } + const txIsLoading = authenticating || processingTx const isButtonDisabled = txIsLoading || couldReceiveNoAssets @@ -108,12 +118,7 @@ export const ConfirmTxScreen = () => { { - closeModal() - InteractionManager.runAfterInteractions(() => { - navigate.submittedTx(signedTx.signedTx.id) - }) - }} + onSuccess={onPasswordTxSuccess} onCancel={closeModal} /> diff --git a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx index c2a392f489..0c0587b126 100644 --- a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx +++ b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx @@ -118,7 +118,8 @@ export const OpenOrders = () => { navigateToTxHistory() } - const onRawTxHwConfirm = () => { + const onRawTxHwConfirm = (order: MappedOpenOrder) => { + trackCancellationSubmitted(order) closeModal() navigateToTxHistory() } @@ -152,7 +153,7 @@ export const OpenOrders = () => { bech32Address={order.owner} onCancel={closeModal} onConfirm={(rootKey) => onRawTxConfirm(rootKey, order)} - onHWConfirm={() => onRawTxHwConfirm()} + onHWConfirm={() => onRawTxHwConfirm(order)} />, 400, ) From d709d45657adb24ca56d948b3325a634aa4bfc37 Mon Sep 17 00:00:00 2001 From: Michal Date: Tue, 21 Nov 2023 14:40:40 +0000 Subject: [PATCH 15/39] fix(swap): Prevent page viewed event from accidentally firing --- .../StartSwapScreen/ListOrders/OpenOrders.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx index 0c0587b126..afbb0ca179 100644 --- a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx +++ b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx @@ -1,10 +1,11 @@ -import {useFocusEffect} from '@react-navigation/native' +import {useNavigation} from '@react-navigation/core' +import {NavigationState, useFocusEffect} from '@react-navigation/native' import {FlashList} from '@shopify/flash-list' import {isString} from '@yoroi/common' import {useSwap, useSwapOrdersByStatusOpen} from '@yoroi/swap' import {Buffer} from 'buffer' import _ from 'lodash' -import React from 'react' +import React, {useRef} from 'react' import {useIntl} from 'react-intl' import {Alert, Linking, StyleSheet, TouchableOpacity, View} from 'react-native' @@ -62,6 +63,7 @@ export const OpenOrders = () => { () => mapOpenOrders(orders, tokenInfos, numberLocale, Object.values(transactionsInfos)), [orders, tokenInfos, numberLocale, transactionsInfos], ) + const navigationRef = useRef(null) const {closeModal, openModal} = useModal() @@ -81,11 +83,17 @@ export const OpenOrders = () => { const {track} = useMetrics() - useFocusEffect( - React.useCallback(() => { - track.swapConfirmedPageViewed({swap_tab: 'Open Orders'}) - }, [track]), - ) + const navigation = useNavigation() + + const trackSwapConfirmPageViewed = React.useCallback(() => { + const currentState = navigation.getState() + const previousState = navigationRef.current + if (currentState === previousState) return + track.swapConfirmedPageViewed({swap_tab: 'Open Orders'}) + navigationRef.current = currentState + }, [track, navigation]) + + useFocusEffect(trackSwapConfirmPageViewed) const trackCancellationSubmitted = (order: MappedOpenOrder) => { track.swapCancelationSubmitted({ From 8cab9ba96b8762762623d5cfe595df0a2c5865ba Mon Sep 17 00:00:00 2001 From: Michal Date: Tue, 21 Nov 2023 14:45:49 +0000 Subject: [PATCH 16/39] Add comment --- .../Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx index afbb0ca179..846ca3f51f 100644 --- a/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx +++ b/apps/wallet-mobile/src/features/Swap/useCases/StartSwapScreen/ListOrders/OpenOrders.tsx @@ -86,6 +86,8 @@ export const OpenOrders = () => { const navigation = useNavigation() const trackSwapConfirmPageViewed = React.useCallback(() => { + // Closing a modal triggers this callback. + // https://github.com/Emurgo/yoroi/pull/2913 const currentState = navigation.getState() const previousState = navigationRef.current if (currentState === previousState) return From ccd32e499ef770d364ee85a8f41ea198d7f644ad Mon Sep 17 00:00:00 2001 From: Juliano Lazzarotto <30806844+stackchain@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:36:03 +0000 Subject: [PATCH 17/39] chore: cocoapods bump --- apps/wallet-mobile/ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wallet-mobile/ios/Podfile.lock b/apps/wallet-mobile/ios/Podfile.lock index b789250ff4..7eff5f118c 100644 --- a/apps/wallet-mobile/ios/Podfile.lock +++ b/apps/wallet-mobile/ios/Podfile.lock @@ -945,4 +945,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 4045625f1556b4d232075df8dbd22524a7010c3f -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.3 From 683323c36332b985de52dab2c4bd2bf1a45d1acf Mon Sep 17 00:00:00 2001 From: Rahul <62512215+rahulnr7@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:55:16 +0530 Subject: [PATCH 18/39] Remove Appium tests from app folder (#2917) --- apps/wallet-mobile/package.json | 6 - apps/wallet-mobile/tests/config/testPaths.ts | 4 - .../tests/config/wdio.android.local.conf.ts | 234 ---------------- apps/wallet-mobile/tests/constants.ts | 91 ------ apps/wallet-mobile/tests/helpers/errors.ts | 2 - apps/wallet-mobile/tests/helpers/utils.ts | 41 --- .../screenFunctions/common.screenFunctions.ts | 68 ----- .../myWallet.screenFunctions.ts | 44 --- .../prepare.screenFunctions.ts | 40 --- .../screenFunctions/send.screenFunctions.ts | 34 --- .../walletHistory.screenFunctions.ts | 81 ------ .../tests/screenObjects/addWallet.screen.ts | 3 - .../tests/screenObjects/alert.screen.ts | 3 - .../screenObjects/chooseLanguage.screen.ts | 1 - .../chooseConnectionMethod.screen.ts | 6 - .../connectToLedgerDevice.screen.ts | 9 - .../createWalletCredentials.screen.ts | 11 - .../nobodyLookingNotification.screen.ts | 1 - .../recoveryPhraseEnter.screen.ts | 1 - .../recoveryPhraseNotification.screen.ts | 5 - .../recoveryPhraseRemember.screen.ts | 10 - .../tests/screenObjects/errorModal.screen.ts | 5 - .../tests/screenObjects/myWallets.screen.ts | 9 - .../tests/screenObjects/pinCode.screen.ts | 2 - .../tests/screenObjects/receive.screen.ts | 6 - .../recoveryPhraseEnterManually.screen.ts | 3 - .../verifyRestoredWallet.screen.ts | 2 - .../selectWalletToRestore.screen.ts | 3 - .../tests/screenObjects/send.screen.ts | 21 -- .../confirmDelegating.screen.ts | 12 - .../confirmWithdrawTransaction.screen.ts | 12 - .../stakingScreens/stakingCenter.screen.ts | 3 - .../stakingScreens/stakingDashboard.screen.ts | 15 - .../stakingScreens/withdrawWarning.screen.ts | 8 - .../tests/screenObjects/tos.screen.ts | 2 - .../screenObjects/walletBottomPanel.screen.ts | 11 - .../screenObjects/walletHistory.screen.ts | 44 --- .../tests/specs/happyPaths.shelley.15.test.ts | 258 ------------------ .../tests/specs/local.ledger.test.ts | 76 ------ 39 files changed, 1187 deletions(-) delete mode 100644 apps/wallet-mobile/tests/config/testPaths.ts delete mode 100644 apps/wallet-mobile/tests/config/wdio.android.local.conf.ts delete mode 100644 apps/wallet-mobile/tests/constants.ts delete mode 100644 apps/wallet-mobile/tests/helpers/errors.ts delete mode 100644 apps/wallet-mobile/tests/helpers/utils.ts delete mode 100644 apps/wallet-mobile/tests/screenFunctions/common.screenFunctions.ts delete mode 100644 apps/wallet-mobile/tests/screenFunctions/myWallet.screenFunctions.ts delete mode 100644 apps/wallet-mobile/tests/screenFunctions/prepare.screenFunctions.ts delete mode 100644 apps/wallet-mobile/tests/screenFunctions/send.screenFunctions.ts delete mode 100644 apps/wallet-mobile/tests/screenFunctions/walletHistory.screenFunctions.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/addWallet.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/alert.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/chooseLanguage.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/chooseConnectionMethod.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/connectToLedgerDevice.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/createWalletScreens/createWalletCredentials.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/createWalletScreens/nobodyLookingNotification.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseEnter.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseNotification.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseRemember.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/errorModal.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/myWallets.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/pinCode.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/receive.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/verifyRestoredWallet.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/selectWalletToRestore.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/send.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmDelegating.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmWithdrawTransaction.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingCenter.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingDashboard.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/stakingScreens/withdrawWarning.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/tos.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/walletBottomPanel.screen.ts delete mode 100644 apps/wallet-mobile/tests/screenObjects/walletHistory.screen.ts delete mode 100644 apps/wallet-mobile/tests/specs/happyPaths.shelley.15.test.ts delete mode 100644 apps/wallet-mobile/tests/specs/local.ledger.test.ts diff --git a/apps/wallet-mobile/package.json b/apps/wallet-mobile/package.json index a15d4e6454..f703f4e9af 100644 --- a/apps/wallet-mobile/package.json +++ b/apps/wallet-mobile/package.json @@ -231,12 +231,6 @@ "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.32.0", - "@wdio/appium-service": "^7.16.6", - "@wdio/cli": "<8.0.0", - "@wdio/local-runner": "^7.16.8", - "@wdio/mocha-framework": "^7.16.6", - "@wdio/selenium-standalone-service": "^7.25.1", - "@wdio/spec-reporter": "^7.16.4", "@yoroi/types": "1.3.0", "babel-eslint": "^10.1.0", "babel-jest": "^29.2.1", diff --git a/apps/wallet-mobile/tests/config/testPaths.ts b/apps/wallet-mobile/tests/config/testPaths.ts deleted file mode 100644 index 6aaf568605..0000000000 --- a/apps/wallet-mobile/tests/config/testPaths.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const artifactsDir = './tests/artifacts/' -export const screenshotsDir = `${artifactsDir}screenshots/` -export const screenRecordsDir = `${artifactsDir}screenRecords/` -export const appiumLogsPath = `${artifactsDir}appium_info.log` diff --git a/apps/wallet-mobile/tests/config/wdio.android.local.conf.ts b/apps/wallet-mobile/tests/config/wdio.android.local.conf.ts deleted file mode 100644 index f68d2d144f..0000000000 --- a/apps/wallet-mobile/tests/config/wdio.android.local.conf.ts +++ /dev/null @@ -1,234 +0,0 @@ -import fs, {promises as fsAsync} from 'fs' -import rimraf from 'rimraf' - -import {APP_ID, APP_ID_PARENT, APP_PATH, VALID_PIN} from '../constants' -import {enterPinCodeIfNecessary, prepareAppIfNecessary} from '../screenFunctions/prepare.screenFunctions'; -import * as myWalletsScreen from "../screenObjects/myWallets.screen"; -import {appiumLogsPath, artifactsDir, screenRecordsDir, screenshotsDir} from './testPaths' - - -export const config: WebdriverIO.Config = { - runner: 'local', - // ============ - // Specs - // ============ - specs: ['./tests/specs/*.test.ts'], - suites: { - happyPathTx: [ - './tests/specs/happyPaths.shelley.15.test.ts' - ], - ledger: [ - './tests/specs/local.ledger.test.ts' - ], - }, - capabilities: [ - { - platformName: 'Android', - maxInstances: 1, - 'appium:deviceName': 'Pixel_5', - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - 'appium:autoLaunch': false, - 'appium:appWaitActivity': APP_ID_PARENT, - 'appium:noReset': true, - 'appium:orientation': 'PORTRAIT', - 'appium:automationName': 'UiAutomator2', - 'appium:app': APP_PATH, - }, - ], - autoCompileOpts: { - autoCompile: true, - // for all available options - tsNodeOpts: { - transpileOnly: true, - project: 'tsconfig.json', - }, - }, - logLevel: 'error', - bail: 0, - baseUrl: 'http://localhost', - waitforTimeout: 10000, - connectionRetryTimeout: 120000, - connectionRetryCount: 3, - services: [ - [ - 'appium', - { - command: 'appium', - logPath: appiumLogsPath, - }, - ], - ['selenium-standalone', {}], - ], - framework: 'mocha', - reporters: ['spec'], - mochaOpts: { - ui: 'bdd', - timeout: 3 * 60 * 1000, // 3min - }, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides several hooks you can use to interfere with the tests process in order to enhance - // it and to build services around it. You can either apply a single function or an array of - // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got - // resolved to continue. - // /** - // * Gets executed once before all workers get launched. - // * @param {Object} config wdio configuration object - // * @param {Array.} capabilities list of capabilities details - // */ - onPrepare: function () { - rimraf.sync(artifactsDir) - fs.mkdirSync(artifactsDir) - }, - /** - * Gets executed before a worker process is spawned and can be used to initialise specific service - * for that worker as well as modify runtime environments in an async fashion. - * @param {String} cid capability id (e.g. 0-0) - * @param {[type]} caps object containing capabilities for session that will be spawn in the worker - * @param {[type]} specs specs to be run in the worker process - * @param {[type]} args object that will be merged with the main configuration once worker is initialised - * @param {[type]} execArgv list of string arguments passed to the worker process - */ - // onWorkerStart: function (cid, caps, specs, args, execArgv) { - // }, - /** - * Gets executed just before initialising the webdriver session and tests framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - beforeSession: function (config, capabilities, specs) { - const testSuitePathArray = specs[0].split('/') - const testSuiteName = testSuitePathArray[testSuitePathArray.length - 1] - capabilities['appium:noReset'] = !testSuiteName.includes('resetApp') - }, - /** - * Gets executed before tests execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - * @param {Object} driver instance of created browser/device session - */ - // before: function (capabilities, specs, driver) { - // }, - /** - * Runs before a WebdriverIO command gets executed. - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - /** - * Hook that gets executed before the suite starts - * @param {Object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * Function to be executed before a tests (in Mocha/Jasmine) starts. - */ - beforeTest: async function () { - driver.startRecordingScreen(); - driver.launchApp() - await prepareAppIfNecessary() - await enterPinCodeIfNecessary(VALID_PIN) - await driver.waitUntil(async () => myWalletsScreen.pageTitle().isDisplayed()) - }, - /** - * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling - * beforeEach in Mocha) - */ - // beforeHook: function (tests, context) { - // }, - /** - * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling - * afterEach in Mocha) - */ - // afterHook: function (tests, context, { error, result, duration, passed, retries }) { - // }, - /** - * Function to be executed after a tests (in Mocha/Jasmine only) - * @param {Object} test tests object - * @param {Object} context scope object the tests was executed with - * @param {Error} error error object in case the tests fails, otherwise `undefined` - // * @param {Any} result.result return object of tests function - // * @param {Number} result.duration duration of tests - // * @param {Boolean} result.passed true if tests has passed, otherwise false - // * @param {Object} result.retries information to spec related retries, e.g. `{ attempts: 0, limit: 0 }` - */ - afterTest: async function (test, context, {error}) { - const screenRecord = await driver.stopRecordingScreen(); - // take a screenshot anytime a tests fails and throws an error - if (error) { - if (!fs.existsSync(screenshotsDir)) { - fs.mkdirSync(screenshotsDir, {recursive: true}) - } - if (!fs.existsSync(screenRecordsDir)) { - fs.mkdirSync(screenRecordsDir, {recursive: true}) - } - const testCaseNameFormatted = `${test.parent.replace(/ /g, '_')}-${test.title.replace(/ /g, '_')}` - const screenshotName = `${testCaseNameFormatted}.png` - const screenRecordName = `${testCaseNameFormatted}.mp4` - const screenshotPath = `${screenshotsDir}${screenshotName}` - const screenRecordPath = `${screenRecordsDir}${screenRecordName}` - const screenshot = await driver.takeScreenshot() - await fsAsync.writeFile(screenshotPath, screenshot, 'base64') - await fsAsync.writeFile(screenRecordPath, screenRecord, 'base64') - } - driver.closeApp() - }, - // /** - // * Hook that gets executed after the suite has ended - // * @param {Object} suite suite details - // */ - // afterSuite: function (suite) { - // }, - // /** - // * Runs after a WebdriverIO command gets executed - // * @param {String} commandName hook command name - // * @param {Array} args arguments that command would receive - // * @param {Number} result 0 - command success, 1 - command error - // * @param {Object} error error object if any - // */ - // afterCommand: function (commandName, args, result, error) { - // }, - // /** - // * Gets executed after all tests are done. You still have access to all global variables from - // * the tests. - // * @param {Number} result 0 - tests pass, 1 - tests fail - // * @param {Array.} capabilities list of capabilities details - // * @param {Array.} specs List of spec file paths that ran - // */ - after: function () { - driver.removeApp(APP_ID) - }, - /** - * Gets executed right after terminating the webdriver session. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers got shut down and the process is about to exit. An error - * thrown in the onComplete hook will result in the tests run failing. - * @param {Object} exitCode 0 - success, 1 - fail - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Object} results object containing tests results - */ - // onComplete: function(exitCode, config, capabilities, results) { - // }, - /** - * Gets executed when a refresh happens. - * @param {String} oldSessionId session ID of the old session - * @param {String} newSessionId session ID of the new session - */ - // onReload: function(oldSessionId, newSessionId) { - // } -} diff --git a/apps/wallet-mobile/tests/constants.ts b/apps/wallet-mobile/tests/constants.ts deleted file mode 100644 index 02e9da948b..0000000000 --- a/apps/wallet-mobile/tests/constants.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {join} from 'path' - -export enum WalletType { - NormalWallet, - DaedalusWallet, -} - -/** - * @property {String} checksum wallet checksum - * @property {String} name wallet name - * @property {Array} phrase wallet recovery phrase - * @property {WalletType} type a 15-word wallet or a 24-word wallet - */ -type RestoredWallet = { - checksum: string - name: string - phrase: string[] - type: WalletType -} - -export const DEFAULT_TIMEOUT = 5000 -export const APP_LOADING_TIMEOUT = 7000 -export const TWO_MINUTES_TIMEOUT = 2 * 60 * 1000 // 2 minutes -export const DEFAULT_INTERVAL = 200 -export const VALID_PIN = '123456' -export const WALLET_NAME = 'Testnet Wallet' -export const LEDGER_WALLET_NAME = 'Test Ledger' -export const SPENDING_PASSWORD = '1234567890' -export const ADA_TOKEN = 'ADA' -export const TADA_TOKEN = 'TADA' -export const STAKE_POOL_ID = 'fe662c24cf56fb98626161f76d231ac50ab7b47dd83986a30c1d4796' -export const APP_ID = 'com.emurgo.nightly' -export const APP_ID_PARENT = 'com.emurgo.*' -export const APP_PATH = join(process.cwd(), '/tests/app/Yoroi-Nightly.apk') -export const NORMAL_15_WORD_WALLET: RestoredWallet = { - checksum: 'CONL-2085', - name: 'RTW-15-word', - phrase: [ - 'ritual', - 'nerve', - 'sweet', - 'social', - 'level', - 'pioneer', - 'cream', - 'artwork', - 'material', - 'three', - 'thumb', - 'melody', - 'zoo', - 'fence', - 'east', - ], - type: WalletType.NormalWallet, -} -export const NORMAL_24_WORD_WALLET: RestoredWallet = { - name: 'RTW-24-word', - checksum: 'CCPL-3231', - phrase: [ - 'like', - 'project', - 'body', - 'tribe', - 'track', - 'wheat', - 'noble', - 'blur', - 'reflect', - 'tomorrow', - 'beach', - 'document', - 'market', - 'enforce', - 'clever', - 'submit', - 'gorilla', - 'hockey', - 'can', - 'surge', - 'fossil', - 'asthma', - 'salmon', - 'cry', - ], - type: WalletType.DaedalusWallet, -} -export const RESTORED_WALLETS: RestoredWallet[] = [ - NORMAL_15_WORD_WALLET, - NORMAL_24_WORD_WALLET, -] diff --git a/apps/wallet-mobile/tests/helpers/errors.ts b/apps/wallet-mobile/tests/helpers/errors.ts deleted file mode 100644 index a6b9b3d20e..0000000000 --- a/apps/wallet-mobile/tests/helpers/errors.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const WALLET_ALREADY_EXISTS = 'You already have a wallet with this name' -export const MUST_BE_FILLED = 'Must be filled' \ No newline at end of file diff --git a/apps/wallet-mobile/tests/helpers/utils.ts b/apps/wallet-mobile/tests/helpers/utils.ts deleted file mode 100644 index 6b4a621155..0000000000 --- a/apps/wallet-mobile/tests/helpers/utils.ts +++ /dev/null @@ -1,41 +0,0 @@ -export async function isElementChecked(element: WebdriverIO.Element): Promise { - await driver.setImplicitTimeout(300) - const result = await element.getAttribute('checked') - console.log(`Element is checked: ${result}`) - - return result === 'true' -} - -export const getAmountFromString = (inputAmount: string): string => { - return inputAmount.split(':')[1].trim().split(' ')[0] -} - -export function getPrettyDate(dateObject: Date = new Date(), dateTimeFormat: string = 'ISO') { - const options: Intl.DateTimeFormatOptions = {year: 'numeric', month: 'short', day: 'numeric'} - switch (dateTimeFormat) { - case 'US': - return dateObject.toLocaleDateString('en-US', options) - default: - // ISO format - return dateObject.toISOString().split('T')[0] - } -} - -export const amPmTo24 = (inputStringTime) => { - const [timePart, ampmPart] = inputStringTime.split(' ') - const [hoursStr, minutes, seconds] = timePart.split(':') - const hours = parseInt(hoursStr, 10) - let newHours = '' - - if (ampmPart == 'AM') { - if (hours == 12) { - newHours = '00' - } else { - newHours = hours < 10 ? `0${hours}` : `${hours}` - } - } else if (ampmPart == 'PM' && hours != 12) { - newHours = (12 + hours).toString() - } - - return `${newHours}:${minutes}:${seconds}` -} diff --git a/apps/wallet-mobile/tests/screenFunctions/common.screenFunctions.ts b/apps/wallet-mobile/tests/screenFunctions/common.screenFunctions.ts deleted file mode 100644 index 4fe16b2fb0..0000000000 --- a/apps/wallet-mobile/tests/screenFunctions/common.screenFunctions.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as errorModal from '../screenObjects/errorModal.screen' -import {DEFAULT_INTERVAL, DEFAULT_TIMEOUT} from '../constants' -import {expect} from 'chai' - -export async function checkForErrors(): Promise { - let isErrorDisplayed - try { - isErrorDisplayed = await errorModal - .errorView() - .waitForDisplayed({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}) - } catch (e) { - // nothing to do, an error didn't appear - isErrorDisplayed = false - } - - if (isErrorDisplayed) { - await errorModal.showErrorMessageButton().click() - await driver.pause(200) - expect(isErrorDisplayed, 'An error appeared').to.be.false - } -} - -export const enterNewValue = async (screenElement: any, newValue: string, hide: boolean = true): Promise => { - await screenElement().clearValue() - await screenElement().addValue(newValue) - if (hide) { - await hideKeyboard() - } -} - -export const hideKeyboard = async (): Promise => { - await driver.hideKeyboard('pressKey', 'Done') -} - -export const scroll = async ( - scrollViewComponent: WebdriverIO.Element, - yStartPoint: number, - yEndPoint: number, - xPoint: number, -): Promise => { - await scrollViewComponent.touchAction([ - { - action: 'press', - x: xPoint, - y: yStartPoint, - }, - { - action: 'wait', - ms: 200, - }, - { - action: 'moveTo', - x: xPoint, - y: yEndPoint, - }, - 'release', - ]) -} - -export const getCoordinateByPercents = async ( - xPointPercentage: number, - yPointPercentage: number, -): Promise<[number, number]> => { - const {width, height} = await driver.getWindowSize() - const xPoint = (width * xPointPercentage) / 100 - const yPoint = (height * yPointPercentage) / 100 - return [xPoint, yPoint] -} diff --git a/apps/wallet-mobile/tests/screenFunctions/myWallet.screenFunctions.ts b/apps/wallet-mobile/tests/screenFunctions/myWallet.screenFunctions.ts deleted file mode 100644 index ee92451945..0000000000 --- a/apps/wallet-mobile/tests/screenFunctions/myWallet.screenFunctions.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as myWallets from '../screenObjects/myWallets.screen' -import * as walletBottomPanel from '../screenObjects/walletBottomPanel.screen' -import * as walletHistory from '../screenObjects/walletHistory.screen' -import * as recoveryPhraseScreen from '../screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen' -import {enterNewValue, hideKeyboard} from './common.screenFunctions' -import {SPENDING_PASSWORD} from '../constants' -import * as createNewWalletCredentialsScreen from '../screenObjects/createWalletScreens/createWalletCredentials.screen' - -export const openWallet = async (walletName: string): Promise => { - const walletButton = await myWallets.getWalletButton(walletName) - await walletButton.click() - await driver.waitUntil(async () => await walletBottomPanel.isDisplayed()) - await driver.waitUntil(async () => await walletHistory.isFullyLoaded()) -} - -export const enterRecoveryPhrase = async (recoveryPhrase: string[]): Promise => { - await driver.waitUntil(async () => await recoveryPhraseScreen.mnemonicInputsView().isDisplayed()) - for (let index = 0; index < recoveryPhrase.length; index++) { - await driver.pause(400) - await hideKeyboard() - const mnemonicInput = await recoveryPhraseScreen.getMnemonicField(index).$('//android.widget.EditText') - await mnemonicInput.setValue(recoveryPhrase[index]) - await driver.pause(200) - // Using the KEYCODE_ENTER for Android. - await driver.pressKeyCode(66) - } -} - -export const repeatRecoveryPhrase = async (phraseArray: string[]): Promise => { - for (const phraseArrayWord of phraseArray) { - const element = await driver.$(`//*[@resource-id="wordBadgeNonTapped-${phraseArrayWord}"]`) - await element.click() - } -} - -export const enterWalletCredentials = async ( - walletName: string, - password: string = SPENDING_PASSWORD, - repeatPassword: string = SPENDING_PASSWORD, -): Promise => { - await enterNewValue(createNewWalletCredentialsScreen.walletNameEdit, walletName) - await enterNewValue(createNewWalletCredentialsScreen.spendingPasswordEdit, password) - await enterNewValue(createNewWalletCredentialsScreen.repeatSpendingPasswordEdit, repeatPassword) -} diff --git a/apps/wallet-mobile/tests/screenFunctions/prepare.screenFunctions.ts b/apps/wallet-mobile/tests/screenFunctions/prepare.screenFunctions.ts deleted file mode 100644 index b357fc11b2..0000000000 --- a/apps/wallet-mobile/tests/screenFunctions/prepare.screenFunctions.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {APP_LOADING_TIMEOUT, DEFAULT_INTERVAL, DEFAULT_TIMEOUT, VALID_PIN} from '../constants' -import * as chooseLanguageScreen from '../screenObjects/chooseLanguage.screen' -import * as tosScreen from '../screenObjects/tos.screen' -import * as myWallets from '../screenObjects/myWallets.screen' -import * as pinCodeScreen from '../screenObjects/pinCode.screen' - -export const prepareAppIfNecessary = async (appPIN: string = VALID_PIN): Promise => { - try { - await chooseLanguageScreen - .chooseLanguageButton() - .waitForExist({timeout: APP_LOADING_TIMEOUT, interval: DEFAULT_INTERVAL}) - await firstAppLaunch(appPIN) - } catch (e) { - // There is no "Choose language" button, nothing to do - } -} - -export const firstAppLaunch = async (appPIN: string = VALID_PIN): Promise => { - await chooseLanguageScreen.chooseLanguageButton().click() - await tosScreen.acceptToSCheckbox().click() - await tosScreen.acceptToSButton().click() - await enterPinCode(appPIN) - await enterPinCode(appPIN) - await driver.waitUntil(async () => await myWallets.isDisplayed()) -} - -export const enterPinCode = async (pinCode: string): Promise => { - for (const pinNumber of pinCode) { - await pinCodeScreen.getPinKey(pinNumber).click() - } -} - -export const enterPinCodeIfNecessary = async (pinCode: string): Promise => { - try { - await pinCodeScreen.getPinKey('1').waitForExist({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}) - await enterPinCode(pinCode) - } catch (e) { - // Pin pad is not shown, nothing to do - } -} diff --git a/apps/wallet-mobile/tests/screenFunctions/send.screenFunctions.ts b/apps/wallet-mobile/tests/screenFunctions/send.screenFunctions.ts deleted file mode 100644 index f940259a03..0000000000 --- a/apps/wallet-mobile/tests/screenFunctions/send.screenFunctions.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as sendScreen from '../screenObjects/send.screen' -import {enterNewValue} from './common.screenFunctions' -import {getAssetByName} from './walletHistory.screenFunctions' -import {ADA_TOKEN, TADA_TOKEN} from '../constants' -import {expect} from 'chai' -import {getAmountFromString} from '../helpers/utils' - -export const prepareTransaction = async (receiverAddress: string, tokenName: string, amount: string): Promise => { - // input receiver address - await enterNewValue(sendScreen.receiverAddressInput, receiverAddress) - if (tokenName !== ADA_TOKEN && tokenName !== TADA_TOKEN) { - await selectTokenForTransaction(tokenName) - } - // input amount - await enterNewValue(sendScreen.amountInput, amount) -} - -export const selectTokenForTransaction = async (tokenName: string): Promise => { - await sendScreen.selectAssetButton().click() - await driver.waitUntil(async () => await sendScreen.assetsListComponent().isDisplayed()) - const token = await getAssetByName(tokenName, sendScreen.assetItemSelector, sendScreen.tokenNameSelector) - expect(token.success).to.be.true - const tokenComponent = token.component - await tokenComponent?.click() - await driver.waitUntil(async () => await sendScreen.amountInput().isDisplayed()) -} - -export const balanceAndFeeIsCalculated = async (): Promise => { - const balanceAfterTxString = await sendScreen.balanceAfterText().getText() - const feeString = await sendScreen.feesText().getText() - const cleanedBalance = getAmountFromString(balanceAfterTxString) - const cleanedFee = getAmountFromString(feeString) - return cleanedBalance !== '-' && cleanedFee !== '-' -} diff --git a/apps/wallet-mobile/tests/screenFunctions/walletHistory.screenFunctions.ts b/apps/wallet-mobile/tests/screenFunctions/walletHistory.screenFunctions.ts deleted file mode 100644 index 32fb151416..0000000000 --- a/apps/wallet-mobile/tests/screenFunctions/walletHistory.screenFunctions.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as walletHistoryScreen from '../screenObjects/walletHistory.screen' -import {expect} from 'chai' -import * as receiveScreen from '../screenObjects/receive.screen' -import {addressTextSelector, unusedAddressRows} from '../screenObjects/receive.screen' -import {amPmTo24, getPrettyDate} from '../helpers/utils' -import {getDateHeaders} from "../screenObjects/walletHistory.screen"; - -export const checkTokenInAssets = async (tokenName) => { - await walletHistoryScreen.assetsTabButton().click() - await driver.waitUntil(async () => (await getAllAssets(walletHistoryScreen.assetItemSelector)).length > 0) - expect( - (await getAssetByName(tokenName, walletHistoryScreen.assetItemSelector, walletHistoryScreen.tokenNameSelector)) - .success, - `The token "${tokenName} is not found in assets"`, - ).to.be.true -} - -export const getAllAssets = async (assetItemSelector: string) => driver.$$(assetItemSelector) - -export const getAssetByName = async (assetName: string, assetItemSelector: string, tokenNameSelector: string) => { - const allAssetsComponents = await getAllAssets(assetItemSelector) - for (const assetComponent of allAssetsComponents) { - const assetComponentName = await assetComponent.$(tokenNameSelector).getText() - if (assetComponentName == assetName) { - return {success: true, component: assetComponent} - } - } - return {success: false, component: null} -} - -export const getReceiveAddress = async (): Promise => { - await walletHistoryScreen.receiveButton().click() - await driver.waitUntil(async () => await receiveScreen.generateNewAddressButton().isDisplayed()) - // copy address - const receiverAddress = await copyFirstUnusedAddress() - // go back to the transactions screen - await driver.back() - return receiverAddress -} - -export const copyFirstUnusedAddress = async (): Promise => unusedAddressRows().$(addressTextSelector).getText() - -export const getLatestTx = async (): Promise => { - const allTxs = await walletHistoryScreen.getAllTransactions() - return allTxs[0] -} - -export const getLatestTxTime = async () => { - const latestTx = await getLatestTx() - const txTimeString = await latestTx.$(walletHistoryScreen.transactionTimeTextSelector).getText() - const curDate = await getLatestDate() - const convertedTime = amPmTo24(txTimeString) - return Date.parse(`${curDate}T${convertedTime}`) -} - -export const getLatestDate = async () => { - const allDates = await getDateHeaders() - const dateString = await allDates[0].getText() - if (dateString == 'Today'){ - return getPrettyDate() - } - if (dateString == 'Yesterday'){ - const date = new Date() - date.setDate(date.getDate() - 1) - return getPrettyDate(date) - } - return getPrettyDate(new Date(dateString)) - -} - -export const waitForNewTransaction = async (latestTxISOTime: number, waitTime: number) => { - const curTime = Date.now() - while (curTime + waitTime > Date.now()) { - const newLatestTxTime = await getLatestTxTime() - if (newLatestTxTime > latestTxISOTime) { - return true - } - await driver.pause(500) - } - return false -} diff --git a/apps/wallet-mobile/tests/screenObjects/addWallet.screen.ts b/apps/wallet-mobile/tests/screenObjects/addWallet.screen.ts deleted file mode 100644 index 97440bf6b6..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/addWallet.screen.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const createWalletButton = () => driver.$('//*[@resource-id="createWalletButton"]') -export const restoreWalletButton = () => driver.$('//*[@resource-id="restoreWalletButton"]') -export const connectLedgerWalletButton = () => driver.$('//*[@resource-id="createLedgerWalletButton"]') \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/alert.screen.ts b/apps/wallet-mobile/tests/screenObjects/alert.screen.ts deleted file mode 100644 index 1f5327f347..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/alert.screen.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const title = () => driver.$('//android.widget.TextView[@resource-id="android:id/alertTitle"]') -export const message = () => driver.$('//android.widget.TextView[@resource-id="android:id/message"]') -export const okButton = () => driver.$('//android.widget.Button[@resource-id="android:id/button1"]') diff --git a/apps/wallet-mobile/tests/screenObjects/chooseLanguage.screen.ts b/apps/wallet-mobile/tests/screenObjects/chooseLanguage.screen.ts deleted file mode 100644 index 8b03e12c7a..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/chooseLanguage.screen.ts +++ /dev/null @@ -1 +0,0 @@ -export const chooseLanguageButton = () => driver.$('//*[@resource-id="chooseLangButton"]') diff --git a/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/chooseConnectionMethod.screen.ts b/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/chooseConnectionMethod.screen.ts deleted file mode 100644 index 25e3266166..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/chooseConnectionMethod.screen.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const connectWithUSBButton = () => driver.$('//*[@resource-id="connectWithUSBButton"]') -export const connectWithBLEButton = () => driver.$('//*[@resource-id="connectWithBLEButton"]') - -export const isDisplayed = async (): Promise => { - return (await connectWithUSBButton().isDisplayed()) && (await connectWithBLEButton().isDisplayed()) -} diff --git a/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/connectToLedgerDevice.screen.ts b/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/connectToLedgerDevice.screen.ts deleted file mode 100644 index 862167bba6..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/connectLedgerScreens/connectToLedgerDevice.screen.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const connectLedgerTitle = () => driver.$('//*[@text="Connect to Ledger Device"]') -export const continueButton = () => driver.$('//*[@resource-id="continueButton"]') - -export const allowUsingLocation = () => driver.$('//*[@resource-id="com.android.permissioncontroller:id/permission_allow_foreground_only_button"]') -export const scanningTitle = () => driver.$('//*[@text="Scanning bluetooth devices..."]') -export const getDevices = () => driver.$$('//android.widget.ScrollView') - -export const walletNameInput = () => driver.$('//android.widget.EditText') -export const saveWalletButton = () => driver.$('//*[@resource-id="saveWalletButton"]') \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/createWalletCredentials.screen.ts b/apps/wallet-mobile/tests/screenObjects/createWalletScreens/createWalletCredentials.screen.ts deleted file mode 100644 index e8a90aae6d..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/createWalletCredentials.screen.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {WALLET_ALREADY_EXISTS, MUST_BE_FILLED} from '../../helpers/errors' - -const getAllEditFields = () => driver.$$('//android.widget.EditText') - -export const credentialsView = () => driver.$('//*[@resource-id="credentialsView"]') -export const walletNameEdit = () => getAllEditFields()[0] -export const walletNameExistsError = () => driver.$(`[text="${WALLET_ALREADY_EXISTS}"]`); -export const walletNameIsEmptyError = () => driver.$(`text="${MUST_BE_FILLED}"`); -export const spendingPasswordEdit = () => getAllEditFields()[1] -export const repeatSpendingPasswordEdit = () => getAllEditFields()[2] -export const continueButton = () => driver.$('//*[@resource-id="walletFormContinueButton"]') diff --git a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/nobodyLookingNotification.screen.ts b/apps/wallet-mobile/tests/screenObjects/createWalletScreens/nobodyLookingNotification.screen.ts deleted file mode 100644 index b0b0a3fb96..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/nobodyLookingNotification.screen.ts +++ /dev/null @@ -1 +0,0 @@ -export const understandButton = () => driver.$('//android.view.ViewGroup[@resource-id="mnemonicExplanationModal"]') diff --git a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseEnter.screen.ts b/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseEnter.screen.ts deleted file mode 100644 index 6dad6312af..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseEnter.screen.ts +++ /dev/null @@ -1 +0,0 @@ -export const confirmButton = () => driver.$('//*[@resource-id="mnemonicCheckScreen::confirm"]') diff --git a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseNotification.screen.ts b/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseNotification.screen.ts deleted file mode 100644 index 4e26031a7e..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseNotification.screen.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const mySecretKeyOnDeviceCheckbox = () => - driver.$('//*[@resource-id="mnemonicBackupImportanceModal::checkBox1"]') -export const recoveringOnlyByPhraseCheckbox = () => - driver.$('//*[@resource-id="mnemonicBackupImportanceModal::checkBox2"]') -export const understandButton = () => driver.$('//*[@resource-id="mnemonicBackupImportanceModal::confirm"]') diff --git a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseRemember.screen.ts b/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseRemember.screen.ts deleted file mode 100644 index dfaca2fe2b..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/createWalletScreens/recoveryPhraseRemember.screen.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const getAllWords = async (): Promise => { - let allWords: string[] = [] - for (let i = 0; i < 15; i++) { - const elementText = await driver.$(`//*[@resource-id="mnemonic-${i}"]`).getText() - allWords.push(elementText) - } - return allWords -} - -export const writtenItDownButton = () => driver.$('//*[@resource-id="mnemonicShowScreen::confirm"]') diff --git a/apps/wallet-mobile/tests/screenObjects/errorModal.screen.ts b/apps/wallet-mobile/tests/screenObjects/errorModal.screen.ts deleted file mode 100644 index caba1c5715..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/errorModal.screen.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const errorView = () => driver.$('//*[@resource-id="errorView"]') -export const showErrorMessageButton = () => driver.$('//*[@resource-id="showErrorLogsButton"]') -export const closeErrorViewButton = () => driver.$('//*[@resource-id="closeErrorModalButton"]') - -export const isDisplayed = async () => await errorView().isDisplayed() \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/myWallets.screen.ts b/apps/wallet-mobile/tests/screenObjects/myWallets.screen.ts deleted file mode 100644 index 12c0b42b98..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/myWallets.screen.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const pageTitle = () => driver.$('[text="My wallets"]') -export const addWalletButton = () => driver.$('//*[@resource-id="addWalletOnHaskellShelleyButton"]') -export const addWalletByronButton = () => driver.$('//*[@resource-id="addWalletOnByronButton"]') -export const addWalletTestnetButton = () => driver.$('[text="ADD WALLET ON TESTNET (SHELLEY-ERA)"]') -export const getWalletButton = (walletName: string) => driver.$(`[text="${walletName}"]`) -export const isDisplayed = () => { - driver.setImplicitTimeout(500) - return pageTitle().isDisplayed() -} diff --git a/apps/wallet-mobile/tests/screenObjects/pinCode.screen.ts b/apps/wallet-mobile/tests/screenObjects/pinCode.screen.ts deleted file mode 100644 index ed9563eb9e..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/pinCode.screen.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const getPinKey = (pinNumber: string) => driver.$(`//*[@resource-id="pinKey${pinNumber}"]`) -export const backspaceButton = () => driver.$('//*[@resource-id="pinKey⌫"]') diff --git a/apps/wallet-mobile/tests/screenObjects/receive.screen.ts b/apps/wallet-mobile/tests/screenObjects/receive.screen.ts deleted file mode 100644 index 239cb6de6a..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/receive.screen.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const generateNewAddressButton = () => driver.$('//*[@resource-id="generateNewReceiveAddressButton"]') -export const unusedAddressRows = () => driver.$('//*[@resource-id="unusedAddress"]') -export const usedAddressRows = () => driver.$('//*[@resource-id="usedAddress"]') -export const addressTextSelector = '//*[@resource-id="addressText"]' -export const verifyAddressButtonSelector = '//*[@resource-id="verifyAddressButton"]' -export const copyAddressButtonSelector = '//*[@resource-id="copyButton"]' \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen.ts b/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen.ts deleted file mode 100644 index baea2f3d23..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const restoreWalletButton = () => driver.$('//*[@resource-id="restoreButton"]') -export const mnemonicInputsView = () => driver.$('//*[@resource-id="mnemonicInputsView"]') -export const getMnemonicField = (index) => driver.$(`//*[@resource-id="mnemonicInput${index}"]`) \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/verifyRestoredWallet.screen.ts b/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/verifyRestoredWallet.screen.ts deleted file mode 100644 index 0795d67bc9..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/restoreWalletsScreens/verifyRestoredWallet.screen.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const walletChecksumText = () => driver.$('//*[@resource-id="walletChecksum"]') -export const continueButton = () => driver.$('//*[@resource-id="verifyWalletContinueButton"]') \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/selectWalletToRestore.screen.ts b/apps/wallet-mobile/tests/screenObjects/selectWalletToRestore.screen.ts deleted file mode 100644 index 912a1ebdb6..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/selectWalletToRestore.screen.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const restoreNormalWalletButton = () => driver.$('//*[@resource-id="restoreNormalWalletButton"]') -export const restore24WordWalletButton = () => driver.$('//*[@resource-id="restore24WordWalletButton"]') -export const restoreReadOnlyWalletButton = () => driver.$('//*[@resource-id="importReadOnlyWalletButton"]') \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/send.screen.ts b/apps/wallet-mobile/tests/screenObjects/send.screen.ts deleted file mode 100644 index 72d771b877..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/send.screen.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const receiverAddressInput = () => driver.$('//*[@resource-id="addressInput"]').$('android.widget.EditText') -export const amountInput = () => driver.$('//*[@resource-id="amountFieldInput"]').$('android.widget.EditText') -export const sendAllCheckbox = () => driver.$('//*[@resource-id="sendAllCheckbox"]') -export const continueButton = () => driver.$('//*[@resource-id="continueButton"]') - -export const feesText = () => driver.$('//*[@resource-id="feesText"]') -export const balanceAfterText = () => driver.$('//*[@resource-id="balanceAfterTxText"]') - -export const receiverAddressText = () => driver.$('//*[@resource-id="receiverAddressText"]') -export const totalAmountText = () => driver.$('//*[@resource-id="totalAmountText"]') -export const selectAssetButton = () => driver.$('//*[@resource-id="selectAssetButton"]') -export const confirmSpendingPasswordInput = () => driver.$('//android.widget.EditText') - -// token selector screen -export const assetsListComponent = () => driver.$('//*[@resource-id="assetsList"]') -export const assetItemSelector = '//*[@resource-id="assetSelectorItem"]' -export const tokenNameSelector = '//*[@resource-id="tokenInfoText"]' -export const tokenFingerPrintSelector = '//*[@resource-id="tokenFingerprintText"]' -export const tokenAmountSelector = '//*[@resource-id="tokenAmountText"]' - -export const confirmTxButton = () => driver.$('//*[@resource-id="confirmTxButton"]') diff --git a/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmDelegating.screen.ts b/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmDelegating.screen.ts deleted file mode 100644 index d19dbaadcc..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmDelegating.screen.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const stakePoolHashText = () => driver.$('//*[@resource-id="stakePoolHashText"]') -export const stakingAmountInput = () => driver.$('//*[@resource-id="stakingAmount"]').$('android.widget.EditText') -export const spendingPasswordInput = () => driver.$('//*[@resource-id="spendingPassword"]').$('android.widget.EditText') -export const confirmDelegationButton = () => driver.$('//*[@resource-id="confirmTxButton"]') - -export const isDisplayed = async () => { - return ( - (await stakePoolHashText().isDisplayed()) && - (await spendingPasswordInput().isDisplayed()) && - (await confirmDelegationButton().isDisplayed()) - ) -} diff --git a/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmWithdrawTransaction.screen.ts b/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmWithdrawTransaction.screen.ts deleted file mode 100644 index 0a22c5b542..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/stakingScreens/confirmWithdrawTransaction.screen.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const confirmTxView = () => driver.$('//*[@resource-id="twoActionView"]') -export const recoveredBalanceText = () => driver.$('//*[@resource-id="recoveredBalanceText"]') -export const feeAmountText = () => driver.$('//*[@resource-id="feeAmountText"]') -export const totalAmountText = () => driver.$('//*[@resource-id="totalAmountText"]') -export const walletPasswordInput = () => - driver.$('//*[@resource-id="walletPasswordInput"]').$('android.widget.EditText') -export const cancelTxButton = () => driver.$('//*[@resource-id="cancelTxButton"]') -export const confirmTxButton = () => driver.$('//*[@resource-id="confirmTxButton"]') - -export const isDisplayed = async () => { - return (await confirmTxView().isDisplayed()) && (await totalAmountText().isDisplayed()) -} diff --git a/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingCenter.screen.ts b/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingCenter.screen.ts deleted file mode 100644 index 364f0e3af7..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingCenter.screen.ts +++ /dev/null @@ -1,3 +0,0 @@ -// nightly -export const nightlyPoolHashInput = () => driver.$('//*[@resource-id="nightlyPoolHashInput"]').$('android.widget.EditText') -export const nightlyDelegateButton = () => driver.$('//*[@resource-id="nightlyDelegateButton"]') \ No newline at end of file diff --git a/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingDashboard.screen.ts b/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingDashboard.screen.ts deleted file mode 100644 index eb6ac72c11..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/stakingScreens/stakingDashboard.screen.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const epochProgressTitleCard = () => driver.$('//*[@resource-id="epochProgressTitleCard"]') -export const userSummaryTitleCard = () => driver.$('//*[@resource-id="userSummaryTitleCard"]') -export const availableFundsText = () => driver.$('//*[@resource-id="userSummaryAvailableFundsText"]') -export const userSummaryRewardsText = () => driver.$('//*[@resource-id="userSummaryRewardsText"]') -export const userSummaryWithdrawButton = () => driver.$('//*[@resource-id="userSummaryWithdrawButton"]') -export const userSummaryDelegatedText = () => driver.$('//*[@resource-id="userSummaryDelegatedText"]') -export const stakePoolInfoTitleCard = () => driver.$('//*[@resource-id="stakePoolInfoTitleCard"]') -export const stakePoolInfoPoolIdText = () => driver.$('//*[@resource-id="stakePoolInfoPoolIdText"]') -export const stakePoolInfoPoolIdCopyButton = () => driver.$('//*[@resource-id="copyButton"]') -export const goToStakingCenterButton = () => driver.$('//*[@resource-id="stakingCenterButton"]') -export const notDelegatedImage = () => driver.$('//*[@resource-id="notDelegatedInfo"]') - -export const isDisplayed = async () => { - return (await epochProgressTitleCard().isDisplayed()) && (await userSummaryTitleCard().isDisplayed()) -} diff --git a/apps/wallet-mobile/tests/screenObjects/stakingScreens/withdrawWarning.screen.ts b/apps/wallet-mobile/tests/screenObjects/stakingScreens/withdrawWarning.screen.ts deleted file mode 100644 index aebe99b486..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/stakingScreens/withdrawWarning.screen.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const warningView = () => driver.$('//*[@resource-id="dangerousActionView"]') -export const iUnderstandCheckbox = () => driver.$('//*[@resource-id="dangerousActionCheckbox"]') -export const keepRegisteredButton = () => driver.$('//*[@resource-id="keepRegisteredButton"]') -export const deregisterButton = () => driver.$('//*[@resource-id="deregisterButton"]') - -export const isDisplayed = async () => { - return await warningView().isDisplayed() -} diff --git a/apps/wallet-mobile/tests/screenObjects/tos.screen.ts b/apps/wallet-mobile/tests/screenObjects/tos.screen.ts deleted file mode 100644 index 5719030c09..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/tos.screen.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const acceptToSCheckbox = () => driver.$('//*[@resource-id="acceptTosCheckbox"]') -export const acceptToSButton = () => driver.$('//*[@resource-id="acceptTosButton"]') diff --git a/apps/wallet-mobile/tests/screenObjects/walletBottomPanel.screen.ts b/apps/wallet-mobile/tests/screenObjects/walletBottomPanel.screen.ts deleted file mode 100644 index a5205022e9..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/walletBottomPanel.screen.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const walletHistoryButton = () => driver.$('//*[@resource-id="walletTabBarButton"]') -export const stakingButton = () => driver.$('//*[@resource-id="stakingTabBarButton"]') -export const menuButton = () => driver.$('//*[@resource-id="menuTabBarButton"]') - -export const isDisplayed = async () => { - return ( - (await walletHistoryButton().isDisplayed()) && - (await stakingButton().isDisplayed()) && - (await menuButton().isDisplayed()) - ) -} diff --git a/apps/wallet-mobile/tests/screenObjects/walletHistory.screen.ts b/apps/wallet-mobile/tests/screenObjects/walletHistory.screen.ts deleted file mode 100644 index b7444fd02d..0000000000 --- a/apps/wallet-mobile/tests/screenObjects/walletHistory.screen.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const sendButton = () => driver.$('//*[@resource-id="sendButton"]') -export const receiveButton = () => driver.$('//*[@resource-id="receiveButton"]') -export const transactionsTabButton = () => driver.$('//*[@resource-id="transactionsTabButton"]') -export const assetsTabButton = () => driver.$('//*[@resource-id="assetsTabButton"]') -export const balanceText = () => driver.$('//*[@resource-id="balanceText"]') -export const pairedTotalText = () => driver.$('//*[@resource-id="pairedTotalText"]') - -// transactions list -export const txsHistoryListComponent = () => driver.$('//*[@resource-id="txHistoryList"]') -export const txsEmptyHistoryComponent = () => driver.$('//*[@resource-id="emptyHistoryComponent"]') -export const txsDayHeaderTextSelector = '//*[@resource-id="dayHeaderText"]' -export const txHistoryListItemSelector = '//*[@resource-id="txHistoryListItem"]' -export const transactionDirectionTextSelector = '//*[@resource-id="transactionDirection"]' -export const transactionAmountComponentSelector = '//*[@resource-id="transactionAmount"]' -export const transactionTimeTextSelector = '//*[@resource-id="submittedAtText"]' -export const transactionTotalAssetsTextSelector = '//*[@resource-id="totalAssetsText"]' - -// assets list -export const assetsListComponent = () => driver.$('//*[@resource-id="assetList"]') -export const assetItemSelector = '//*[@resource-id="assetItem"]' -export const tokenNameSelector = '//*[@resource-id="tokenInfoText"]' -export const tokenFingerPrintSelector = '//*[@resource-id="tokenFingerprintText"]' -export const tokenAmountSelector = '//*[@resource-id="tokenAmount"]' - -export const isDisplayed = async () => { - return ( - (await sendButton().isDisplayed()) && - (await receiveButton().isDisplayed()) && - (await transactionsTabButton().isDisplayed()) && - (await assetsTabButton().isDisplayed()) - ) -} - -export const getAllTransactions = async () => driver.$$(txHistoryListItemSelector) - -export const getDateHeaders = async () => driver.$$(txsDayHeaderTextSelector) - -export const isFullyLoaded = async () => { - return ( - (await isDisplayed()) && - (await txsHistoryListComponent().isDisplayed()) && - ((await getAllTransactions()).length > 0) - ) -} diff --git a/apps/wallet-mobile/tests/specs/happyPaths.shelley.15.test.ts b/apps/wallet-mobile/tests/specs/happyPaths.shelley.15.test.ts deleted file mode 100644 index 567805b567..0000000000 --- a/apps/wallet-mobile/tests/specs/happyPaths.shelley.15.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -import * as addWalletScreen from '../screenObjects/addWallet.screen' -import * as createNewWalletCredentialsScreen from '../screenObjects/createWalletScreens/createWalletCredentials.screen' -import * as nobodyLookingScreen from '../screenObjects/createWalletScreens/nobodyLookingNotification.screen' -import * as recoveryPhraseRememberScreen from '../screenObjects/createWalletScreens/recoveryPhraseRemember.screen' -import * as recoveryPhraseNotificationScreen from '../screenObjects/createWalletScreens/recoveryPhraseNotification.screen' -import * as recoveryPhraseEnterScreen from '../screenObjects/createWalletScreens/recoveryPhraseEnter.screen' -import * as myWalletsScreen from '../screenObjects/myWallets.screen' -import * as walletHistoryScreen from '../screenObjects/walletHistory.screen' -import * as sendScreen from '../screenObjects/send.screen' -import * as selectWalletToRestoreScreen from '../screenObjects/selectWalletToRestore.screen' -import * as recoveryPhraseInputScreen from '../screenObjects/restoreWalletsScreens/recoveryPhraseEnterManually.screen' -import * as verifyRestoredWalletScreen from '../screenObjects/restoreWalletsScreens/verifyRestoredWallet.screen' -import * as walletBottomPanel from '../screenObjects/walletBottomPanel.screen' -import * as stakingDashboard from '../screenObjects/stakingScreens/stakingDashboard.screen' -import * as stakingCenter from '../screenObjects/stakingScreens/stakingCenter.screen' -import * as confirmDelegationScreen from '../screenObjects/stakingScreens/confirmDelegating.screen' -import * as withdrawWarningScreen from '../screenObjects/stakingScreens/withdrawWarning.screen' -import * as confirmWithdrawTransactionScreen from '../screenObjects/stakingScreens/confirmWithdrawTransaction.screen' -import { - WALLET_NAME, - DEFAULT_TIMEOUT, - DEFAULT_INTERVAL, - VALID_PIN, - NORMAL_15_WORD_WALLET, - WalletType, - SPENDING_PASSWORD, - TADA_TOKEN, - TWO_MINUTES_TIMEOUT, - STAKE_POOL_ID, -} from '../constants' -import { - checkForErrors, - enterNewValue, - getCoordinateByPercents, - hideKeyboard, - scroll, -} from '../screenFunctions/common.screenFunctions' -import { - enterRecoveryPhrase, - enterWalletCredentials, - openWallet, - repeatRecoveryPhrase, -} from '../screenFunctions/myWallet.screenFunctions' -import { - checkTokenInAssets, - getLatestTxTime, - getReceiveAddress, - waitForNewTransaction, -} from '../screenFunctions/walletHistory.screenFunctions' -import {prepareTransaction, balanceAndFeeIsCalculated} from '../screenFunctions/send.screenFunctions' -import {enterPinCodeIfNecessary} from '../screenFunctions/prepare.screenFunctions' -import {AssertionError} from 'chai' - -const expect = require('chai').expect - -describe('Happy paths', () => { - describe('Creating a wallet', () => { - it('Shelley era', async () => { - await myWalletsScreen.addWalletTestnetButton().click() - await addWalletScreen.createWalletButton().click() - - await driver.waitUntil(async () => await createNewWalletCredentialsScreen.credentialsView().isDisplayed()) - await enterWalletCredentials(WALLET_NAME) - await hideKeyboard() - await createNewWalletCredentialsScreen.continueButton().click() - - await nobodyLookingScreen.understandButton().click() - - const allWords = await recoveryPhraseRememberScreen.getAllWords() - await recoveryPhraseRememberScreen.writtenItDownButton().click() - - await recoveryPhraseNotificationScreen.mySecretKeyOnDeviceCheckbox().click() - await recoveryPhraseNotificationScreen.recoveringOnlyByPhraseCheckbox().click() - await recoveryPhraseNotificationScreen.understandButton().click() - - await repeatRecoveryPhrase(allWords) - await recoveryPhraseEnterScreen.confirmButton().click() - - await enterPinCodeIfNecessary(VALID_PIN) - await driver.waitUntil(async () => await myWalletsScreen.pageTitle().isDisplayed()) - - expect( - await driver.$(`[text="${WALLET_NAME}"]`).waitForExist({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}), - ).to.be.true - }) - }) - - describe('Restored wallet', () => { - it(`Restoring ${NORMAL_15_WORD_WALLET.name} wallet`, async () => { - await myWalletsScreen.addWalletTestnetButton().click() - await addWalletScreen.restoreWalletButton().click() - - if (NORMAL_15_WORD_WALLET.type == WalletType.NormalWallet) { - await selectWalletToRestoreScreen.restoreNormalWalletButton().click() - } else if (NORMAL_15_WORD_WALLET.type == WalletType.DaedalusWallet) { - await selectWalletToRestoreScreen.restore24WordWalletButton().click() - } else { - throw Error(`Unknown wallet type: wallet type is ${NORMAL_15_WORD_WALLET.type}`) - } - await enterRecoveryPhrase(NORMAL_15_WORD_WALLET.phrase) - await hideKeyboard() - await recoveryPhraseInputScreen.restoreWalletButton().click() - - expect(await verifyRestoredWalletScreen.walletChecksumText().isDisplayed()).to.be.true - expect(await verifyRestoredWalletScreen.walletChecksumText().getText()).to.be.equal( - NORMAL_15_WORD_WALLET.checksum, - ) - await verifyRestoredWalletScreen.continueButton().click() - - await driver.waitUntil(async () => await createNewWalletCredentialsScreen.credentialsView().isDisplayed()) - await enterWalletCredentials(NORMAL_15_WORD_WALLET.name) - await createNewWalletCredentialsScreen.continueButton().click() - - await driver.waitUntil(async () => await myWalletsScreen.pageTitle().isDisplayed()) - - expect( - await driver - .$(`[text="${NORMAL_15_WORD_WALLET.name}"]`) - .waitForExist({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}), - `The "${NORMAL_15_WORD_WALLET.name}" wasn't found`, - ).to.be.true - }) - - it(`Intrawallet transaction, ${NORMAL_15_WORD_WALLET.name} wallet`, async () => { - await openWallet(NORMAL_15_WORD_WALLET.name) - const latestTxTime = await getLatestTxTime() - const receiverAddress = await getReceiveAddress() - await walletHistoryScreen.sendButton().click() - await prepareTransaction(receiverAddress, TADA_TOKEN, '1') - await driver.waitUntil(async () => await balanceAndFeeIsCalculated()) - await sendScreen.continueButton().click() - await driver.waitUntil(async () => await sendScreen.confirmTxButton().isDisplayed()) - await enterNewValue(sendScreen.confirmSpendingPasswordInput, SPENDING_PASSWORD) - await sendScreen.confirmTxButton().click() - - await checkForErrors() - - expect( - await walletHistoryScreen.sendButton().waitForDisplayed({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}), - `Wallet transactions screen is not displayed`, - ).to.be.true - - try { - await driver.waitUntil(async () => await waitForNewTransaction(latestTxTime, TWO_MINUTES_TIMEOUT)) - } catch (e) { - throw new AssertionError('There is no new transaction') - } - }) - - it(`Intrawallet transaction, ${NORMAL_15_WORD_WALLET.name} wallet, token`, async () => { - const tokenName = 'wDOGE' - await openWallet(NORMAL_15_WORD_WALLET.name) - const latestTxTime = await getLatestTxTime() - await checkTokenInAssets(tokenName) - const receiverAddress = await getReceiveAddress() - await walletHistoryScreen.sendButton().click() - await prepareTransaction(receiverAddress, tokenName, '1') - await driver.waitUntil(async () => await balanceAndFeeIsCalculated()) - await sendScreen.continueButton().click() - await driver.waitUntil(async () => await sendScreen.confirmTxButton().isDisplayed()) - await enterNewValue(sendScreen.confirmSpendingPasswordInput, SPENDING_PASSWORD) - await sendScreen.confirmTxButton().click() - - await checkForErrors() - - expect( - await walletHistoryScreen.sendButton().waitForDisplayed({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}), - `Wallet transactions screen is not displayed`, - ).to.be.true - - try { - await driver.waitUntil(async () => await waitForNewTransaction(latestTxTime, TWO_MINUTES_TIMEOUT)) - } catch (e) { - throw new AssertionError('There is no new transaction') - } - }) - - it(`Delegation, ${NORMAL_15_WORD_WALLET.name} wallet`, async () => { - await openWallet(NORMAL_15_WORD_WALLET.name) - const latestTxTime = await getLatestTxTime() - - await walletBottomPanel.stakingButton().click() - await driver.waitUntil(async () => await stakingDashboard.isDisplayed()) - await stakingDashboard.goToStakingCenterButton().click() - await enterNewValue(stakingCenter.nightlyPoolHashInput, STAKE_POOL_ID) - await stakingCenter.nightlyDelegateButton().click() - await driver.waitUntil(async () => await confirmDelegationScreen.isDisplayed()) - const stakePoolHash = await confirmDelegationScreen.stakePoolHashText().getText() - expect(stakePoolHash).to.be.equal(STAKE_POOL_ID) - await enterNewValue(confirmDelegationScreen.spendingPasswordInput, SPENDING_PASSWORD) - await confirmDelegationScreen.confirmDelegationButton().click() - - await checkForErrors() - - try { - await driver.waitUntil(async () => await waitForNewTransaction(latestTxTime, TWO_MINUTES_TIMEOUT + 60000)) - } catch (e) { - throw new AssertionError('There is no new transaction') - } - - await walletBottomPanel.stakingButton().click() - await driver.waitUntil(async () => await stakingDashboard.isDisplayed()) - const availableFunds = await stakingDashboard.availableFundsText().getText() - const totalDelegated = await stakingDashboard.userSummaryDelegatedText().getText() - - expect(availableFunds).to.be.equal(totalDelegated) - }) - - it(`Deregistering, ${NORMAL_15_WORD_WALLET.name} wallet`, async () => { - await openWallet(NORMAL_15_WORD_WALLET.name) - const latestTxTime = await getLatestTxTime() - await walletBottomPanel.stakingButton().click() - - await driver.waitUntil(async () => await stakingDashboard.isDisplayed()) - const availableFunds = await stakingDashboard.availableFundsText().getText() - const totalDelegated = await stakingDashboard.userSummaryDelegatedText().getText() - expect(availableFunds).to.be.equal(totalDelegated) - await stakingDashboard.userSummaryWithdrawButton().click() - - await driver.waitUntil(async () => await withdrawWarningScreen.isDisplayed()) - const [xPoint, yStartPoint] = await getCoordinateByPercents(50, 80) - const [, yEndPoint] = await getCoordinateByPercents(50, 30) - - await scroll(await withdrawWarningScreen.warningView(), yStartPoint, yEndPoint, xPoint) - - await withdrawWarningScreen.iUnderstandCheckbox().click() - await withdrawWarningScreen.deregisterButton().click() - - await driver.waitUntil(async () => await confirmWithdrawTransactionScreen.isDisplayed()) - const recoveredBalance = (await confirmWithdrawTransactionScreen.recoveredBalanceText().getText()).split(' ')[0] - expect(parseFloat(recoveredBalance)).to.be.equal(2) - await enterNewValue(confirmWithdrawTransactionScreen.walletPasswordInput, SPENDING_PASSWORD, false) - - const [, yStartPointTxScreen] = await getCoordinateByPercents(50, 40) - const [, yEndPointTxScreen] = await getCoordinateByPercents(50, 10) - - await scroll( - await confirmWithdrawTransactionScreen.confirmTxView(), - yStartPointTxScreen, - yEndPointTxScreen, - xPoint, - ) - await confirmWithdrawTransactionScreen.confirmTxButton().click() - - await checkForErrors() - - try { - await driver.waitUntil(async () => await waitForNewTransaction(latestTxTime, TWO_MINUTES_TIMEOUT)) - } catch (e) { - throw new AssertionError('There is no new transaction') - } - - await walletBottomPanel.stakingButton().click() - await driver.waitUntil(async () => await stakingDashboard.isDisplayed()) - expect(await stakingDashboard.notDelegatedImage().isDisplayed()).to.be.true - }) - }) -}) diff --git a/apps/wallet-mobile/tests/specs/local.ledger.test.ts b/apps/wallet-mobile/tests/specs/local.ledger.test.ts deleted file mode 100644 index e78aef37a4..0000000000 --- a/apps/wallet-mobile/tests/specs/local.ledger.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {DEFAULT_INTERVAL, DEFAULT_TIMEOUT, TWO_MINUTES_TIMEOUT, LEDGER_WALLET_NAME, TADA_TOKEN} from '../constants' -import * as addWalletScreen from '../screenObjects/addWallet.screen' -import * as chooseConnectionMethod from '../screenObjects/connectLedgerScreens/chooseConnectionMethod.screen' -import * as connectToLedgerDevice from '../screenObjects/connectLedgerScreens/connectToLedgerDevice.screen' -import * as myWalletsScreen from '../screenObjects/myWallets.screen' -import {AssertionError, expect} from 'chai' -import * as sendScreen from '../screenObjects/send.screen' -import * as walletHistoryScreen from '../screenObjects/walletHistory.screen' -import {checkForErrors, enterNewValue} from '../screenFunctions/common.screenFunctions' -import {openWallet} from '../screenFunctions/myWallet.screenFunctions' -import { - getLatestTxTime, - getReceiveAddress, - waitForNewTransaction -} from '../screenFunctions/walletHistory.screenFunctions' -import {balanceAndFeeIsCalculated, prepareTransaction} from '../screenFunctions/send.screenFunctions' - -describe('HW Ledger wallet', () => { - it('Connect a wallet', async () => { - await myWalletsScreen.addWalletTestnetButton().click() - await addWalletScreen.connectLedgerWalletButton().click() - await chooseConnectionMethod.connectWithBLEButton().click() - - await driver.waitUntil(async () => await connectToLedgerDevice.connectLedgerTitle().isDisplayed()) - await driver.waitUntil(async () => await connectToLedgerDevice.continueButton().isDisplayed()) - await connectToLedgerDevice.continueButton().click() - await connectToLedgerDevice.allowUsingLocation().click() - await driver.waitUntil(async () => await connectToLedgerDevice.scanningTitle().isDisplayed()) - await driver.pause(500) - const allScrollViews = await connectToLedgerDevice.getDevices() - await allScrollViews[allScrollViews.length - 1].$('android.view.ViewGroup').click() - - await driver.waitUntil(async () => await connectToLedgerDevice.saveWalletButton().isDisplayed()) - await enterNewValue(connectToLedgerDevice.walletNameInput, LEDGER_WALLET_NAME) - await connectToLedgerDevice.saveWalletButton().click() - await driver.waitUntil(async () => await myWalletsScreen.pageTitle().isDisplayed()) - - expect( - await driver - .$(`[text="${LEDGER_WALLET_NAME}"]`) - .waitForExist({timeout: DEFAULT_TIMEOUT, interval: DEFAULT_INTERVAL}), - `The "${LEDGER_WALLET_NAME}" wasn't found`, - ).to.be.true - }) - - it('Send intrawallet transaction', async () => { - await openWallet(LEDGER_WALLET_NAME) - const latestTxTime = await getLatestTxTime() - const receiverAddress = await getReceiveAddress() - await walletHistoryScreen.sendButton().click() - await prepareTransaction(receiverAddress, TADA_TOKEN, '1') - await driver.waitUntil(async () => await balanceAndFeeIsCalculated()) - await sendScreen.continueButton().click() - // choose connection method - await driver.waitUntil(async () => await chooseConnectionMethod.isDisplayed()) - await chooseConnectionMethod.connectWithBLEButton().click() - await driver.waitUntil(async () => await sendScreen.confirmTxButton().isDisplayed()) - await sendScreen.confirmTxButton().click() - - // checking there is no errors after pressing the Confirm TX button - await checkForErrors() - - expect( - await walletHistoryScreen - .sendButton() - .waitForDisplayed({timeout: TWO_MINUTES_TIMEOUT, interval: DEFAULT_INTERVAL}), - `Wallet transactions screen is not displayed`, - ).to.be.true - - try { - await driver.waitUntil(async () => await waitForNewTransaction(latestTxTime, TWO_MINUTES_TIMEOUT)) - } catch (e) { - throw new AssertionError('There is no new transaction') - } - }) -}) From 42d3cea92a6595abfa1cf5308800659472465bbc Mon Sep 17 00:00:00 2001 From: Sorin Chis Date: Fri, 24 Nov 2023 16:39:56 +0200 Subject: [PATCH 19/39] fix: update local theme colors (#2914) --- apps/wallet-mobile/src/theme/darkTheme.ts | 2 + apps/wallet-mobile/src/theme/lightTheme.ts | 2 + .../src/theme/palettes/darkPalette.ts | 73 ++++++++++--------- .../src/theme/palettes/lightPalette.ts | 13 +++- apps/wallet-mobile/src/theme/spacing.ts | 16 ++++ apps/wallet-mobile/src/theme/types.ts | 25 ++++++- 6 files changed, 91 insertions(+), 40 deletions(-) create mode 100644 apps/wallet-mobile/src/theme/spacing.ts diff --git a/apps/wallet-mobile/src/theme/darkTheme.ts b/apps/wallet-mobile/src/theme/darkTheme.ts index 998e8a678b..02ca3ce57a 100644 --- a/apps/wallet-mobile/src/theme/darkTheme.ts +++ b/apps/wallet-mobile/src/theme/darkTheme.ts @@ -1,8 +1,10 @@ import {darkPalette} from './palettes' +import {spacing} from './spacing' import {Theme} from './types' import {typography} from './typography' export const darkTheme: Theme = { color: darkPalette, typography: typography, + spacing: spacing, } diff --git a/apps/wallet-mobile/src/theme/lightTheme.ts b/apps/wallet-mobile/src/theme/lightTheme.ts index 40c0aa1b85..2c9965beb2 100644 --- a/apps/wallet-mobile/src/theme/lightTheme.ts +++ b/apps/wallet-mobile/src/theme/lightTheme.ts @@ -1,8 +1,10 @@ import {lightPalette} from './palettes' +import {spacing} from './spacing' import {Theme} from './types' import {typography} from './typography' export const lightTheme: Theme = { color: lightPalette, typography: typography, + spacing: spacing, } diff --git a/apps/wallet-mobile/src/theme/palettes/darkPalette.ts b/apps/wallet-mobile/src/theme/palettes/darkPalette.ts index fb5d05e249..22428dfffc 100644 --- a/apps/wallet-mobile/src/theme/palettes/darkPalette.ts +++ b/apps/wallet-mobile/src/theme/palettes/darkPalette.ts @@ -4,58 +4,63 @@ export const darkPalette: Palette = { 'black-static': '#000000', 'white-static': '#FFFFFF', gray: { - max: '#000000', - 900: '#242838', - 800: '#383E54', - 700: '#4A5065', - 600: '#6B7384', - 500: '#8A92A3', - 400: '#A7AFC0', - 300: '#C4CAD7', - 200: '#DCE0E9', - 100: '#EAEDF2', - 50: '#F0F3F5', - min: '#FFFFFF', + max: '#FFFFFF', + 900: '#E1E6F5', + 800: '#BCC5E0', + 700: '#9BA4C2', + 600: '#7C85A3', + 500: '#656C85', + 400: '#4B5266', + 300: '#3E4457', + 200: '#262A38', + 100: '#1F232E', + 50: '#15171F', + min: '#0B0B0F', }, primary: { - 900: '#121F4D', - 800: '#122770', - 700: '#1737A3', - 600: '#3154CB', + 900: '#E4E8F7', + 800: '#C4CFF5', + 700: '#A0B3F2', + 600: '#7892E8', 500: '#4B6DDE', - 400: '#7892E8', - 300: '#A0B3F2', - 200: '#C4CFF5', - 100: '#E4E8F7', + 400: '#3154CB', + 300: '#223987', + 200: '#142049', + 100: '#111935', }, secondary: { - 900: '#17453C', - 800: '#12705D', - 700: '#0B997D', - 600: '#08C29D', + 900: '#E4F7F3', + 800: '#C6F7ED', + 700: '#93F5E1', + 600: '#66F2D6', 500: '#16E3BA', - 400: '#66F2D6', - 300: '#93F5E1', - 200: '#C6F7ED', - 100: '#E4F7F3', + 400: '#08C29D', + 300: '#0B997D', + 200: '#12705D', + 100: '#17453C', }, magenta: { + 700: '#FF6B92', + 600: '#FD3468', 500: '#FF1351', - 300: '#FBCBD7', - 100: '#FFF1F5', + 300: '#572835', + 100: '#2F171D', }, cyan: { - 400: '#59B1F4', + 500: '#59B1F4', 100: '#F2F9FF', }, yellow: { - 500: '#F5C70F', - 100: '#FDF8E2', + 500: '#ECBA09', + 100: '#31290E', }, gradients: { 'blue-green': ['#E4E8F7', '#C6F7F7'], green: ['#93F5E1', '#C6F7F7'], blue: ['#244ABF', '#4B6DDE'], }, - overlay: {hex: '#000000', opacity: 0.1}, + 'overlay-extension': {hex: '#15171F', opacity: 0.72}, + 'overlay-mobile': {hex: '#15171F', opacity: 0.64}, + 'sidebar-overlay': {hex: '#ffffff', opacity: 0.24}, + 'sidebar-item': {hex: '#ffffff', opacity: 0.48}, } diff --git a/apps/wallet-mobile/src/theme/palettes/lightPalette.ts b/apps/wallet-mobile/src/theme/palettes/lightPalette.ts index c1269e8350..e048738ba7 100644 --- a/apps/wallet-mobile/src/theme/palettes/lightPalette.ts +++ b/apps/wallet-mobile/src/theme/palettes/lightPalette.ts @@ -40,22 +40,27 @@ export const lightPalette: Palette = { 100: '#E4F7F3', }, magenta: { + 700: '#CF053A', + 600: '#E80742', 500: '#FF1351', 300: '#FBCBD7', 100: '#FFF1F5', }, cyan: { - 400: '#59B1F4', + 500: '#59B1F4', 100: '#F2F9FF', }, yellow: { - 500: '#F5C70F', - 100: '#FDF8E2', + 500: '#ECBA09', + 100: '#FDF7E2', }, gradients: { 'blue-green': ['#E4E8F7', '#C6F7F7'], green: ['#93F5E1', '#C6F7F7'], blue: ['#244ABF', '#4B6DDE'], }, - overlay: {hex: '#000000', opacity: 0.1}, + 'overlay-extension': {hex: '#121f4d', opacity: 0.7}, + 'overlay-mobile': {hex: '#000000', opacity: 0.4}, + 'sidebar-overlay': {hex: '#000000', opacity: 0.16}, + 'sidebar-item': {hex: '#ffffff', opacity: 0.48}, } diff --git a/apps/wallet-mobile/src/theme/spacing.ts b/apps/wallet-mobile/src/theme/spacing.ts new file mode 100644 index 0000000000..ab3275a599 --- /dev/null +++ b/apps/wallet-mobile/src/theme/spacing.ts @@ -0,0 +1,16 @@ +import {Spacing} from './types' + +export const spacing: Spacing = { + 'spacing-2': '2px', + 'spacing-4': '4px', + 'spacing-6': '6px', + 'spacing-8': '8px', + 'spacing-10': '10px', + 'spacing-12': '12px', + 'spacing-16': '16px', + 'spacing-24': '24px', + 'spacing-32': '32px', + 'spacing-40': '40px', + 'spacing-48': '48px', + 'spacing-80': '80px', +} diff --git a/apps/wallet-mobile/src/theme/types.ts b/apps/wallet-mobile/src/theme/types.ts index 20889564d7..37dfe50281 100644 --- a/apps/wallet-mobile/src/theme/types.ts +++ b/apps/wallet-mobile/src/theme/types.ts @@ -3,6 +3,7 @@ import {TextStyle} from 'react-native' export type Theme = { color: Palette typography: Typography + spacing: Spacing } export type HexColor = `#${string}` @@ -49,12 +50,14 @@ export type Palette = { 100: HexColor } magenta: { + 700: HexColor + 600: HexColor 500: HexColor 300: HexColor 100: HexColor } cyan: { - 400: HexColor + 500: HexColor 100: HexColor } yellow: { @@ -66,7 +69,10 @@ export type Palette = { green: Gradient blue: Gradient } - overlay: {hex: HexColor; opacity: number} + 'overlay-extension': {hex: HexColor; opacity: number} + 'overlay-mobile': {hex: HexColor; opacity: number} + 'sidebar-overlay': {hex: HexColor; opacity: number} + 'sidebar-item': {hex: HexColor; opacity: number} } type TypographyKeys = @@ -97,4 +103,19 @@ type TypographyKeys = | 'caption-medium' | 'caption-regular' +type SpacingKeys = + | 'spacing-2' + | 'spacing-4' + | 'spacing-6' + | 'spacing-8' + | 'spacing-10' + | 'spacing-12' + | 'spacing-16' + | 'spacing-24' + | 'spacing-32' + | 'spacing-40' + | 'spacing-48' + | 'spacing-80' + export type Typography = Record +export type Spacing = Record From 18b460189a22b642778d410a0243bae613e682c4 Mon Sep 17 00:00:00 2001 From: Juliano Lazzarotto <30806844+stackchain@users.noreply.github.com> Date: Mon, 27 Nov 2023 10:39:12 +0000 Subject: [PATCH 20/39] feature(links): new links package (#2911) --- metro.config.js | 1 + package.json | 1 + packages/api/.gitignore | 1 + packages/banxa/.gitignore | 1 + packages/common/.gitignore | 1 + packages/common/src/helpers/parsers.test.ts | 21 + packages/common/src/helpers/parsers.ts | 5 + packages/links/.dependency-cruiser.js | 449 ++++++++++++++++++++ packages/links/.gitignore | 71 ++++ packages/links/.watchmanconfig | 1 + packages/links/README.md | 71 ++++ packages/links/babel.config.js | 3 + packages/links/jest.setup.js | 5 + packages/links/package.json | 219 ++++++++++ packages/links/scripts/flowgen.sh | 3 + packages/links/src/cardano/helpers.ts | 5 + packages/links/src/cardano/module.test.ts | 396 +++++++++++++++++ packages/links/src/cardano/module.ts | 143 +++++++ packages/links/src/cardano/params.ts | 156 +++++++ packages/links/src/cardano/types.ts | 31 ++ packages/links/src/index.ts | 2 + packages/links/tsconfig.build.json | 5 + packages/links/tsconfig.json | 25 ++ packages/swap/.gitignore | 1 + packages/types/src/index.ts | 32 ++ packages/types/src/links/errors.ts | 7 + packages/types/src/links/link.ts | 24 ++ yoroi.code-workspace | 4 + 28 files changed, 1684 insertions(+) create mode 100644 packages/links/.dependency-cruiser.js create mode 100644 packages/links/.gitignore create mode 100644 packages/links/.watchmanconfig create mode 100644 packages/links/README.md create mode 100644 packages/links/babel.config.js create mode 100644 packages/links/jest.setup.js create mode 100644 packages/links/package.json create mode 100644 packages/links/scripts/flowgen.sh create mode 100644 packages/links/src/cardano/helpers.ts create mode 100644 packages/links/src/cardano/module.test.ts create mode 100644 packages/links/src/cardano/module.ts create mode 100644 packages/links/src/cardano/params.ts create mode 100644 packages/links/src/cardano/types.ts create mode 100644 packages/links/src/index.ts create mode 100644 packages/links/tsconfig.build.json create mode 100644 packages/links/tsconfig.json create mode 100644 packages/types/src/links/errors.ts create mode 100644 packages/types/src/links/link.ts diff --git a/metro.config.js b/metro.config.js index 00983fbf4c..cb363785a3 100644 --- a/metro.config.js +++ b/metro.config.js @@ -6,6 +6,7 @@ module.exports = { path.resolve(__dirname, "node_modules"), path.resolve(__dirname, "packages/types"), path.resolve(__dirname, "packages/banxa"), + path.resolve(__dirname, "packages/links"), path.resolve(__dirname, "packages/common"), path.resolve(__dirname, "packages/api"), path.resolve(__dirname, "packages/openswap"), diff --git a/package.json b/package.json index 84d9451c40..1386c04e9f 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "packages/types", "packages/openswap", "packages/common", + "packages/links", "packages/api", "packages/*", "apps/*", diff --git a/packages/api/.gitignore b/packages/api/.gitignore index 75356714f9..32c6d7db77 100644 --- a/packages/api/.gitignore +++ b/packages/api/.gitignore @@ -7,6 +7,7 @@ # VSCode .vscode/ +!.vscode/launch.json jsconfig.json # Xcode diff --git a/packages/banxa/.gitignore b/packages/banxa/.gitignore index 75356714f9..32c6d7db77 100644 --- a/packages/banxa/.gitignore +++ b/packages/banxa/.gitignore @@ -7,6 +7,7 @@ # VSCode .vscode/ +!.vscode/launch.json jsconfig.json # Xcode diff --git a/packages/common/.gitignore b/packages/common/.gitignore index 75356714f9..32c6d7db77 100644 --- a/packages/common/.gitignore +++ b/packages/common/.gitignore @@ -7,6 +7,7 @@ # VSCode .vscode/ +!.vscode/launch.json jsconfig.json # Xcode diff --git a/packages/common/src/helpers/parsers.test.ts b/packages/common/src/helpers/parsers.test.ts index b631207f2e..a42c85c901 100644 --- a/packages/common/src/helpers/parsers.test.ts +++ b/packages/common/src/helpers/parsers.test.ts @@ -15,6 +15,7 @@ import { parseSafe, parseString, parseNumber, + isUrl, } from './parsers' describe('parsers', () => { @@ -47,6 +48,26 @@ describe('parsers', () => { }) }) + describe('isUrl', () => { + it('returns true if string is a valid url', () => { + expect(isUrl('https://domiain.com')).toEqual(true) + expect(isUrl('http://domain.com')).toEqual(true) + expect(isUrl('bitcoin://domain.com/path')).toEqual(true) + expect(isUrl('ipfs://domain.com/path?query=string')).toEqual(true) + expect(isUrl('https://domain.com/path?query=string#hash')).toEqual(true) + }) + + it('returns false if string is not a valid url', () => { + expect(isUrl('hello')).toEqual(false) + expect(isUrl('123')).toEqual(false) + expect(isUrl({})).toEqual(false) + expect(isUrl([])).toEqual(false) + expect(isUrl(null)).toEqual(false) + expect(isUrl(undefined)).toEqual(false) + expect(isUrl(true)).toEqual(false) + }) + }) + describe('isRecord', () => { it('returns true if is an object', () => { expect(isRecord({})).toEqual(true) diff --git a/packages/common/src/helpers/parsers.ts b/packages/common/src/helpers/parsers.ts index 208f950855..da64d9848b 100644 --- a/packages/common/src/helpers/parsers.ts +++ b/packages/common/src/helpers/parsers.ts @@ -62,6 +62,11 @@ export const isArray = createTypeGuardFromSchema( z.array(z.unknown()), ) +export const urlSchema = z.string().url() +export const isUrl = (data: unknown): data is string => { + return urlSchema.safeParse(data).success +} + export function isArrayOfType( data: unknown, predicate: (data: unknown) => data is T, diff --git a/packages/links/.dependency-cruiser.js b/packages/links/.dependency-cruiser.js new file mode 100644 index 0000000000..fff414129a --- /dev/null +++ b/packages/links/.dependency-cruiser.js @@ -0,0 +1,449 @@ +/** @type {import('dependency-cruiser').IConfiguration} */ +module.exports = { + forbidden: [ + /* rules from the 'recommended' preset: */ + { + name: 'fix-circular', + severity: 'error', + comment: + 'This dependency is part of a circular relationship. You might want to revise ' + + 'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ', + from: {}, + to: { + circular: true + } + }, + { + name: 'no-orphans', + comment: + "This is an orphan module - it's likely not used (anymore?). Either use it or " + + "remove it. If it's logical this module is an orphan (i.e. it's a config file), " + + "add an exception for it in your dependency-cruiser configuration. By default " + + "this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " + + "files (.d.ts), tsconfig.json and some of the babel and webpack configs.", + severity: 'warn', + from: { + orphan: true, + pathNot: [ + '(^|/)\\.[^/]+\\.(js|cjs|mjs|ts|json)$', // dot files + '\\.d\\.ts$', // TypeScript declaration files + '(^|/)tsconfig\\.json$', // TypeScript config + '(^|/)(babel|webpack)\\.config\\.(js|cjs|mjs|ts|json)$' // other configs + ] + }, + to: {}, + }, + { + name: 'no-deprecated-core', + comment: + 'A module depends on a node core module that has been deprecated. Find an alternative - these are ' + + "bound to exist - node doesn't deprecate lightly.", + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'core' + ], + path: [ + '^(v8\/tools\/codemap)$', + '^(v8\/tools\/consarray)$', + '^(v8\/tools\/csvparser)$', + '^(v8\/tools\/logreader)$', + '^(v8\/tools\/profile_view)$', + '^(v8\/tools\/profile)$', + '^(v8\/tools\/SourceMap)$', + '^(v8\/tools\/splaytree)$', + '^(v8\/tools\/tickprocessor-driver)$', + '^(v8\/tools\/tickprocessor)$', + '^(node-inspect\/lib\/_inspect)$', + '^(node-inspect\/lib\/internal\/inspect_client)$', + '^(node-inspect\/lib\/internal\/inspect_repl)$', + '^(async_hooks)$', + '^(punycode)$', + '^(domain)$', + '^(constants)$', + '^(sys)$', + '^(_linklist)$', + '^(_stream_wrap)$' + ], + } + }, + { + name: 'not-to-deprecated', + comment: + 'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' + + 'version of that module, or find an alternative. Deprecated modules are a security risk.', + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'deprecated' + ] + } + }, + { + name: 'no-non-package-json', + severity: 'error', + comment: + "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " + + "That's problematic as the package either (1) won't be available on live (2 - worse) will be " + + "available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " + + "in your package.json.", + from: {}, + to: { + dependencyTypes: [ + 'npm-no-pkg', + 'npm-unknown' + ] + } + }, + { + name: 'not-to-unresolvable', + comment: + "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " + + 'module: add it to your package.json. In all other cases you likely already know what to do.', + severity: 'error', + from: {}, + to: { + couldNotResolve: true + } + }, + { + name: 'no-duplicate-dep-types', + comment: + "Likely this module depends on an external ('npm') package that occurs more than once " + + "in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " + + "maintenance problems later on.", + severity: 'warn', + from: {}, + to: { + moreThanOneDependencyType: true, + // as it's pretty common to have a type import be a type only import + // _and_ (e.g.) a devDependency - don't consider type-only dependency + // types for this rule + dependencyTypesNot: ["type-only"] + } + }, + + /* rules you might want to tweak for your specific situation: */ + { + name: 'not-to-spec', + comment: + 'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' + + "If there's something in a spec that's of use to other modules, it doesn't have that single " + + 'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.', + severity: 'error', + from: {}, + to: { + path: '\\.(spec|test)\\.(js|mjs|cjs|ts|ls|coffee|litcoffee|coffee\\.md)$' + } + }, + { + name: 'not-to-dev-dep', + severity: 'error', + comment: + "This module depends on an npm package from the 'devDependencies' section of your " + + 'package.json. It looks like something that ships to production, though. To prevent problems ' + + "with npm packages that aren't there on production declare it (only!) in the 'dependencies'" + + 'section of your package.json. If this module is development only - add it to the ' + + 'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration', + from: { + path: '^(src)', + pathNot: '\\.(spec|test)\\.(js|mjs|cjs|ts|ls|coffee|litcoffee|coffee\\.md)$' + }, + to: { + dependencyTypes: [ + 'npm-dev' + ] + } + }, + { + name: 'optional-deps-used', + severity: 'info', + comment: + "This module depends on an npm package that is declared as an optional dependency " + + "in your package.json. As this makes sense in limited situations only, it's flagged here. " + + "If you're using an optional dependency here by design - add an exception to your" + + "dependency-cruiser configuration.", + from: {}, + to: { + dependencyTypes: [ + 'npm-optional' + ] + } + }, + { + name: 'peer-deps-used', + comment: + "This module depends on an npm package that is declared as a peer dependency " + + "in your package.json. This makes sense if your package is e.g. a plugin, but in " + + "other cases - maybe not so much. If the use of a peer dependency is intentional " + + "add an exception to your dependency-cruiser configuration.", + severity: 'warn', + from: {}, + to: { + dependencyTypes: [ + 'npm-peer' + ] + } + } + ], + options: { + + /* conditions specifying which files not to follow further when encountered: + - path: a regular expression to match + - dependencyTypes: see https://github.com/sverweij/dependency-cruiser/blob/master/doc/rules-reference.md#dependencytypes-and-dependencytypesnot + for a complete list + */ + doNotFollow: { + path: 'node_modules' + }, + + /* conditions specifying which dependencies to exclude + - path: a regular expression to match + - dynamic: a boolean indicating whether to ignore dynamic (true) or static (false) dependencies. + leave out if you want to exclude neither (recommended!) + */ + // exclude : { + // path: '', + // dynamic: true + // }, + + /* pattern specifying which files to include (regular expression) + dependency-cruiser will skip everything not matching this pattern + */ + // includeOnly : '', + + /* dependency-cruiser will include modules matching against the focus + regular expression in its output, as well as their neighbours (direct + dependencies and dependents) + */ + // focus : '', + + /* list of module systems to cruise */ + // moduleSystems: ['amd', 'cjs', 'es6', 'tsd'], + + /* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/develop/' + to open it on your online repo or `vscode://file/${process.cwd()}/` to + open it in visual studio code), + */ + // prefix: '', + + /* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation + true: also detect dependencies that only exist before typescript-to-javascript compilation + "specify": for each dependency identify whether it only exists before compilation or also after + */ + tsPreCompilationDeps: true, + + /* + list of extensions to scan that aren't javascript or compile-to-javascript. + Empty by default. Only put extensions in here that you want to take into + account that are _not_ parsable. + */ + // extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"], + + /* if true combines the package.jsons found from the module up to the base + folder the cruise is initiated from. Useful for how (some) mono-repos + manage dependencies & dependency definitions. + */ + // combinedDependencies: false, + + /* if true leave symlinks untouched, otherwise use the realpath */ + // preserveSymlinks: false, + + /* TypeScript project file ('tsconfig.json') to use for + (1) compilation and + (2) resolution (e.g. with the paths property) + + The (optional) fileName attribute specifies which file to take (relative to + dependency-cruiser's current working directory). When not provided + defaults to './tsconfig.json'. + */ + tsConfig: { + fileName: 'tsconfig.json' + }, + + /* Webpack configuration to use to get resolve options from. + + The (optional) fileName attribute specifies which file to take (relative + to dependency-cruiser's current working directory. When not provided defaults + to './webpack.conf.js'. + + The (optional) `env` and `args` attributes contain the parameters to be passed if + your webpack config is a function and takes them (see webpack documentation + for details) + */ + // webpackConfig: { + // fileName: './webpack.config.js', + // env: {}, + // args: {}, + // }, + + /* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use + for compilation (and whatever other naughty things babel plugins do to + source code). This feature is well tested and usable, but might change + behavior a bit over time (e.g. more precise results for used module + systems) without dependency-cruiser getting a major version bump. + */ + // babelConfig: { + // fileName: './.babelrc' + // }, + + /* List of strings you have in use in addition to cjs/ es6 requires + & imports to declare module dependencies. Use this e.g. if you've + re-declared require, use a require-wrapper or use window.require as + a hack. + */ + // exoticRequireStrings: [], + /* options to pass on to enhanced-resolve, the package dependency-cruiser + uses to resolve module references to disk. You can set most of these + options in a webpack.conf.js - this section is here for those + projects that don't have a separate webpack config file. + + Note: settings in webpack.conf.js override the ones specified here. + */ + enhancedResolveOptions: { + /* List of strings to consider as 'exports' fields in package.json. Use + ['exports'] when you use packages that use such a field and your environment + supports it (e.g. node ^12.19 || >=14.7 or recent versions of webpack). + + If you have an `exportsFields` attribute in your webpack config, that one + will have precedence over the one specified here. + */ + exportsFields: ["exports"], + /* List of conditions to check for in the exports field. e.g. use ['imports'] + if you're only interested in exposed es6 modules, ['require'] for commonjs, + or all conditions at once `(['import', 'require', 'node', 'default']`) + if anything goes for you. Only works when the 'exportsFields' array is + non-empty. + + If you have a 'conditionNames' attribute in your webpack config, that one will + have precedence over the one specified here. + */ + conditionNames: ["import", "require", "node", "default"], + /* + The extensions, by default are the same as the ones dependency-cruiser + can access (run `npx depcruise --info` to see which ones that are in + _your_ environment. If that list is larger than what you need (e.g. + it contains .js, .jsx, .ts, .tsx, .cts, .mts - but you don't use + TypeScript you can pass just the extensions you actually use (e.g. + [".js", ".jsx"]). This can speed up the most expensive step in + dependency cruising (module resolution) quite a bit. + */ + // extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], + /* + If your TypeScript project makes use of types specified in 'types' + fields in package.jsons of external dependencies, specify "types" + in addition to "main" in here, so enhanced-resolve (the resolver + dependency-cruiser uses) knows to also look there. You can also do + this if you're not sure, but still use TypeScript. In a future version + of dependency-cruiser this will likely become the default. + */ + mainFields: ["main", "types"], + }, + reporterOptions: { + dot: { + /* pattern of modules that can be consolidated in the detailed + graphical dependency graph. The default pattern in this configuration + collapses everything in node_modules to one folder deep so you see + the external modules, but not the innards your app depends upon. + */ + collapsePattern: 'node_modules/[^/]+', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/master/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + don't worry - dependency-cruiser will fall back to the default one. + */ + // theme: { + // graph: { + // /* use splines: "ortho" for straight lines. Be aware though + // graphviz might take a long time calculating ortho(gonal) + // routings. + // */ + // splines: "true" + // }, + // modules: [ + // { + // criteria: { matchesFocus: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesFocus: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { matchesReaches: true }, + // attributes: { + // fillcolor: "lime", + // penwidth: 2, + // }, + // }, + // { + // criteria: { matchesReaches: false }, + // attributes: { + // fillcolor: "lightgrey", + // }, + // }, + // { + // criteria: { source: "^src/model" }, + // attributes: { fillcolor: "#ccccff" } + // }, + // { + // criteria: { source: "^src/view" }, + // attributes: { fillcolor: "#ccffcc" } + // }, + // ], + // dependencies: [ + // { + // criteria: { "rules[0].severity": "error" }, + // attributes: { fontcolor: "red", color: "red" } + // }, + // { + // criteria: { "rules[0].severity": "warn" }, + // attributes: { fontcolor: "orange", color: "orange" } + // }, + // { + // criteria: { "rules[0].severity": "info" }, + // attributes: { fontcolor: "blue", color: "blue" } + // }, + // { + // criteria: { resolved: "^src/model" }, + // attributes: { color: "#0000ff77" } + // }, + // { + // criteria: { resolved: "^src/view" }, + // attributes: { color: "#00770077" } + // } + // ] + // } + }, + archi: { + /* pattern of modules that can be consolidated in the high level + graphical dependency graph. If you use the high level graphical + dependency graph reporter (`archi`) you probably want to tweak + this collapsePattern to your situation. + */ + collapsePattern: '^(packages|src|lib|app|bin|test(s?)|spec(s?))/[^/]+|node_modules/[^/]+', + + /* Options to tweak the appearance of your graph.See + https://github.com/sverweij/dependency-cruiser/blob/master/doc/options-reference.md#reporteroptions + for details and some examples. If you don't specify a theme + for 'archi' dependency-cruiser will use the one specified in the + dot section (see above), if any, and otherwise use the default one. + */ + // theme: { + // }, + }, + "text": { + "highlightFocused": true + }, + } + } +}; +// generated: dependency-cruiser@12.10.0 on 2023-03-08T01:53:10.874Z \ No newline at end of file diff --git a/packages/links/.gitignore b/packages/links/.gitignore new file mode 100644 index 0000000000..32c6d7db77 --- /dev/null +++ b/packages/links/.gitignore @@ -0,0 +1,71 @@ +# OSX +# +.DS_Store + +# XDE +.expo/ + +# VSCode +.vscode/ +!.vscode/launch.json +jsconfig.json + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# Cocoapods +# +example/ios/Pods + +# Ruby +example/vendor/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Expo +.expo/ + +# Turborepo +.turbo/ + +# generated by bob +lib/ diff --git a/packages/links/.watchmanconfig b/packages/links/.watchmanconfig new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/links/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/packages/links/README.md b/packages/links/README.md new file mode 100644 index 0000000000..1be06b18a3 --- /dev/null +++ b/packages/links/README.md @@ -0,0 +1,71 @@ +# @yoroi/links + +## Overview +This TypeScript package provides type-safe way to create and parse cryptocurrency-related links based on its URI definition (ABNF). It is designed to support different types of operations. It supports Cardano operations such as claims and legacy transfers within its blockchain ecosystem. It allows apps to interpret and build crypto links. + +## Features +- **Custom URI Scheme Handling**: Supports the 'web+cardano' URI scheme, tailored for Cardano blockchain interactions. +- **Type-Safe Link Creation and Parsing**: Utilizes TypeScript for ensuring the integrity and correctness of link structures. +- **Configurable for Different Operations/Chains**: Includes configurations for claim operations (`configCardanoClaimV1`) and legacy transfers (`configCardanoLegacyTransfer`). + +## Installation +How to install the package, e.g., via npm or yarn: +```bash +npm install @yoroi/links +# or +yarn add @yoroi/links +``` + +## Usage +### Importing the Module +```typescript +import { linksCardanoModuleMaker } from '@yoroi/links'; +``` + +### How to create a link +```typescript +const { create } = linksCardanoModuleMaker(); + +const cardanoLink = create({ + config: configCardanoClaimV1, + params: { + faucet_url: "https://someendpoint/airdrop", + code: "CardanoSummit2024", + other: "other param" + } +}); + +console.log(cardanoLink.link); +``` + +### How to parse a link +```typescript +const { parse } = linksCardanoModuleMaker(); + +const parsedLink = parse('web+cardano://claim/v1?code=123&faucet_url=http://example.com'); +console.log(parsedLink); +``` + +## API reference + +### Interface +- `create`: Creates a crypto link based on the provided configuration and parameters. +- `parse`: Parses a given string into a Link object if supported, otherwise will throw. + +### Built-in configurations +- `configCardanoClaimV1`: Configuration for Cardano `claim` operations. [CIP99](https://github.com/cardano-foundation/CIPs/pull/546/files) +- `configCardanoLegacyTransfer`: Configuration for Cardano `legacy` transfers. [CIP13](https://cips.cardano.org/cips/cip13/) + +## For more +- [BIP-21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) +- [EIP-681](https://eips.ethereum.org/EIPS/eip-681) +- [URI](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwiGtpWV-eOCAxVSmokEHdBOAn0QFnoECBQQAQ&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FUniform_Resource_Identifier&usg=AOvVaw2i8uSyn7gtMV9bW4Nmh4dK&opi=89978449) +- [ABNF](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjYq-3u-OOCAxVxvokEHTx1CqsQFnoECBIQAQ&url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FAugmented_Backus%25E2%2580%2593Naur_form&usg=AOvVaw3GEFuH6Hby-NUw6cxQpQUz&opi=89978449) +- [RFC-2234](https://datatracker.ietf.org/doc/html/rfc2234) + +## Supported Schemes and Authorities + +| Scheme | Authority | Description | +|----------------|-----------|---------------------------------------------| +| `web+cardano` | `claim` | Used for proof of onboarding / airdrops | +| `web+cardano` | ` ` | Used for legacy payment requests | \ No newline at end of file diff --git a/packages/links/babel.config.js b/packages/links/babel.config.js new file mode 100644 index 0000000000..f842b77fcf --- /dev/null +++ b/packages/links/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], +}; diff --git a/packages/links/jest.setup.js b/packages/links/jest.setup.js new file mode 100644 index 0000000000..10deefd07a --- /dev/null +++ b/packages/links/jest.setup.js @@ -0,0 +1,5 @@ +jest.mock('@react-native-async-storage/async-storage', () => + require('@react-native-async-storage/async-storage/jest/async-storage-mock'), +) + +jest.setTimeout(30000) \ No newline at end of file diff --git a/packages/links/package.json b/packages/links/package.json new file mode 100644 index 0000000000..92b88e8533 --- /dev/null +++ b/packages/links/package.json @@ -0,0 +1,219 @@ +{ + "name": "@yoroi/links", + "version": "1.3.0", + "description": "The package for managing links of Yoroi SDK", + "keywords": [ + "yoroi", + "cardano", + "links", + "browser", + "react", + "cip13", + "cip99" + ], + "homepage": "https://github.com/Emurgo/yoroi/packages/links#readme", + "bugs": { + "url": "https://github.com/Emurgo/yoroi/issues" + }, + "repository": { + "type": "github", + "url": "https://github.com/Emurgo/yoroi.git", + "directory": "packages/links" + }, + "license": "Apache-2.0", + "author": "EMURGO Fintech (https://github.com/Emurgo/yoroi)", + "contributors": [ + { + "name": "Juliano Lazzarotto", + "email": "30806844+stackchain@users.noreply.github.com" + } + ], + "main": "lib/commonjs/index", + "module": "lib/module/index", + "source": "src/index", + "browser": "lib/module/index", + "types": "lib/typescript/index.d.ts", + "files": [ + "src", + "lib", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "build": "yarn tsc && yarn lint && yarn test --ci --silent && yarn clean && bob build", + "build:release": "yarn build && yarn flow", + "clean": "del-cli lib", + "dev": "yarn clean && bob build", + "dgraph": "depcruise src --include-only \"^src\" --output-type dot | dot -T svg > dependency-graph.svg", + "flow": ". ./scripts/flowgen.sh", + "lint": "eslint \"**/*.{js,ts,tsx}\"", + "prepack": "yarn build:release", + "prepublish:beta": "yarn build:release", + "publish:beta": "npm publish --scope yoroi --tag beta --access beta", + "prepublish:prod": "yarn build:release", + "publish:prod": "npm publish --scope yoroi --access public", + "release": "release-it", + "test": "jest", + "test:watch": "jest --watch", + "tsc": "tsc --noEmit -p tsconfig.json" + }, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "prettier": { + "bracketSpacing": false, + "quoteProps": "consistent", + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false + }, + "eslintConfig": { + "extends": [ + "@react-native-community", + "prettier" + ], + "rules": { + "prettier/prettier": [ + "error", + { + "quoteProps": "consistent", + "bracketSpacing": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "semi": false + } + ] + }, + "root": true + }, + "eslintIgnore": [ + "node_modules/", + "lib/", + "babel.config.js", + "jest.setup.js", + "coverage/" + ], + "jest": { + "collectCoverage": true, + "collectCoverageFrom": [ + "src/**/*.{js,jsx,ts,tsx}", + "!src/**/*.d.ts", + "!src/**/*.mocks.{js,jsx,ts,tsx}" + ], + "coverageReporters": [ + "text-summary", + "html" + ], + "coverageThreshold": { + "global": { + "branches": 100, + "functions": 100, + "lines": 100, + "statements": 100 + } + }, + "modulePathIgnorePatterns": [ + "/example/node_modules", + "/lib/" + ], + "preset": "react-native", + "setupFiles": [ + "/jest.setup.js" + ] + }, + "dependencies": { + "@yoroi/common": "1.3.0", + "@yoroi/types": "1.3.0" + }, + "devDependencies": { + "@commitlint/config-conventional": "^17.0.2", + "@react-native-async-storage/async-storage": "^1.19.3", + "@react-native-community/eslint-config": "^3.0.2", + "@release-it/conventional-changelog": "^5.0.0", + "@testing-library/react-hooks": "^8.0.1", + "@testing-library/react-native": "^12.3.0", + "@types/jest": "^28.1.2", + "@types/react": "18.2.0", + "@types/react-native": "0.71.6", + "@yoroi/types": "1.3.0", + "commitlint": "^17.0.2", + "del-cli": "^5.0.0", + "dependency-cruiser": "^13.1.1", + "eslint": "^8.4.1", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-ft-flow": "^3.0.0", + "eslint-plugin-prettier": "^4.0.0", + "flowgen": "^1.21.0", + "jest": "^29.7.0", + "pod-install": "^0.1.0", + "prettier": "^2.0.5", + "react": "18.2.0", + "react-native": "~0.71.0", + "react-native-builder-bob": "^0.20.4", + "react-query": "^3.39.3", + "react-test-renderer": "^18.2.0", + "release-it": "^15.0.0", + "typescript": "^4.5.2" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": ">= 1.19.3 <= 1.20.0", + "react": ">= 16.8.0 <= 19.0.0", + "react-query": "^3.39.3" + }, + "optionalDependencies": { + "@react-native-async-storage/async-storage": "^1.19.3" + }, + "packageManager": "yarn@1.22.21", + "engines": { + "node": ">= 16.19.0" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json", + "tsc": "./node_modules/.bin/tsc" + } + ] + ] + }, + "release-it": { + "git": { + "commitMessage": "chore: release ${version}", + "tagName": "v${version}" + }, + "npm": { + "publish": true + }, + "github": { + "release": false + }, + "plugins": { + "@release-it/conventional-changelog": { + "preset": "angular" + } + } + } +} diff --git a/packages/links/scripts/flowgen.sh b/packages/links/scripts/flowgen.sh new file mode 100644 index 0000000000..2638ef8d27 --- /dev/null +++ b/packages/links/scripts/flowgen.sh @@ -0,0 +1,3 @@ +for i in $(find lib -type f -name "*.d.ts"); + do sh -c "npx flowgen $i -o ${i%.*.*}.js.flow"; +done; diff --git a/packages/links/src/cardano/helpers.ts b/packages/links/src/cardano/helpers.ts new file mode 100644 index 0000000000..2ff2ecaef1 --- /dev/null +++ b/packages/links/src/cardano/helpers.ts @@ -0,0 +1,5 @@ +import {isString} from '@yoroi/common' + +// TODO: validate address with headless +export const isCardanoAddress = (address: string) => + isString(address) && /^[A-Za-z_0-9]+$/.test(address) diff --git a/packages/links/src/cardano/module.test.ts b/packages/links/src/cardano/module.test.ts new file mode 100644 index 0000000000..f0aba64a0e --- /dev/null +++ b/packages/links/src/cardano/module.test.ts @@ -0,0 +1,396 @@ +import {Links} from '@yoroi/types' +import { + configCardanoClaimV1, + configCardanoLegacyTransfer, + linksCardanoModuleMaker, +} from './module' + +describe('linksCardanoModuleMaker', () => { + it('should return a Links.Module', () => { + const module = linksCardanoModuleMaker() + expect(module).toBeDefined() + }) + + describe('.create()', () => { + const module = linksCardanoModuleMaker() + + describe('claim v1', () => { + it('should throw if missing required params', () => { + try { + module.create({ + config: configCardanoClaimV1, + params: {}, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.RequiredParamsMissing) + expect( + (error as Links.Errors.RequiredParamsMissing).message, + ).toContain('param code') + } + try { + module.create({ + config: configCardanoClaimV1, + params: {code: '123'}, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.RequiredParamsMissing) + expect( + (error as Links.Errors.RequiredParamsMissing).message, + ).toContain('param faucet_url') + } + }) + + it('should throw if params are invalid', () => { + try { + module.create({ + config: configCardanoClaimV1, + params: {code: 123, faucet_url: 'https://faucet.com'}, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param code') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a string') + } + try { + module.create({ + config: configCardanoClaimV1, + params: {code: 'https://faucet.com', faucet_url: 123}, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param faucet_url') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('valid url') + } + }) + + // param that might fail as optional will pass as extra + it('should ignore extra params type checking and just include them', () => { + const link = module.create({ + config: configCardanoClaimV1, + params: { + code: '300', + faucet_url: 'https://faucet.com', + memo: 1, + message: 1, + amount: '-,NaN', + }, + }) + expect(link).toEqual({ + config: configCardanoClaimV1, + params: { + code: '300', + faucet_url: 'https://faucet.com', + memo: 1, + message: 1, + amount: '-,NaN', + }, + link: 'web+cardano://claim/v1?code=300&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN', + }) + }) + + it('should throw if a forbiden param was provided', () => { + try { + module.create({ + config: configCardanoClaimV1, + params: {code: '123', faucet_url: 'https://faucet.com', address: 1}, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ForbiddenParamsProvided) + expect( + (error as Links.Errors.ForbiddenParamsProvided).message, + ).toContain('param address') + } + }) + }) + + describe('legacy transfer', () => { + it('should work when none optional params were provided', () => { + const link = module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }, + }) + expect(link).toEqual({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }, + link: 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }) + }) + + it('should work when optional params were provided and should drop extra params without throwing', () => { + const link = module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: '%$-_/.memo', + message: ['%$-_/.', 'message'], + extra: 'extra', + }, + }) + expect(link).toEqual({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: '%$-_/.memo', + message: ['%$-_/.', 'message'], + }, + link: 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1.23&memo=%25%24-_%2F.memo&message=%25%24-_%2F.&message=message', + }) + const link2 = module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: '%$-_/.memo', + message: '%$-_/.message', + extra: 'extra', + }, + }) + expect(link2).toEqual({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: '%$-_/.memo', + message: '%$-_/.message', + }, + link: 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1.23&memo=%25%24-_%2F.memo&message=%25%24-_%2F.message', + }) + }) + + it('should throw if optional params are invalid', () => { + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: '1,23', + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param amount') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a number') + } + + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: 1, + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param memo') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a string') + } + + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_/test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param address') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a cardano address') + } + + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: 'memo', + message: 1, + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param message') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a string or array of strings') + } + + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: 'memo', + message: [], + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param message') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a string or array of strings') + } + + try { + module.create({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: 'memo', + message: [1], + }, + }) + } catch (error) { + expect(error).toBeInstanceOf(Links.Errors.ParamsValidationFailed) + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('param message') + expect( + (error as Links.Errors.ParamsValidationFailed).message, + ).toContain('must be a string or array of strings') + } + }) + }) + }) + describe('.parse()', () => { + const module = linksCardanoModuleMaker() + // NOTE: UnsupportedScheme is not tested here since is part of the manager parser (not implemented yet) + it('should return undefined if scheme is part of cardano parser', () => { + const url = + 'bitcoin:1BoatSLRHtKNngkdXEeobR76b53LETtpyT?amount=0.01&label=JohnDoe&message=Payment%20for%20services' + expect(module.parse(url)).toBeUndefined() + }) + + it('should throw ParamsValidationFailed (params)', () => { + const url = + 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1,23&memo=%25%24-_%2F.memo&message=%25%24-_%2F.messagei' + expect(() => module.parse(url)).toThrow( + Links.Errors.ParamsValidationFailed, + ) + }) + + it('should throw ParamsValidationFailed (legacy address)', () => { + const url = + 'web+cardano:addr_/test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1,23&memo=%25%24-_%2F.memo&message=%25%24-_%2F.messagei' + expect(() => module.parse(url)).toThrow( + Links.Errors.ParamsValidationFailed, + ) + }) + + it('should throw ForbiddenParamsProvided', () => { + const url = + 'web+cardano://claim/v1?code=300&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1' + expect(() => module.parse(url)).toThrow( + Links.Errors.ForbiddenParamsProvided, + ) + }) + + it('should throw UnsupportedVersion', () => { + const url = + 'web+cardano://claim/v2?code=300&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1' + expect(() => module.parse(url)).toThrow(Links.Errors.UnsupportedVersion) + }) + + it('should throw UnsupportedAuthority', () => { + const url = + 'web+cardano://authority/v2?code=300&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1' + expect(() => module.parse(url)).toThrow(Links.Errors.UnsupportedAuthority) + }) + + it('should work and keep extra params when allowed', () => { + const url = + 'web+cardano://claim/v1?code=300&faucet_url=https%3A%2F%2Ffaucet.com&memo=memo-text&message=message1&message=message2&message=message3&extra=extra' + const link = module.parse(url) + expect(link).toEqual({ + config: configCardanoClaimV1, + params: { + code: '300', + faucet_url: 'https://faucet.com', + memo: 'memo-text', + message: ['message1', 'message2', 'message3'], + extra: 'extra', + }, + link: url, + }) + }) + + it('should work and drop extra params when set to', () => { + const url = + 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?extra=extra&amount=1.23&memo=memo&message=message' + const link = module.parse(url) + expect(link).toEqual({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + amount: 1.23, + memo: 'memo', + message: 'message', + }, + link: 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1.23&memo=memo&message=message', + }) + }) + + it('should work minimum legacy transfer', () => { + const url = + 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km' + const link = module.parse(url) + expect(link).toEqual({ + config: configCardanoLegacyTransfer, + params: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }, + link: 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + }) + }) + }) +}) diff --git a/packages/links/src/cardano/module.ts b/packages/links/src/cardano/module.ts new file mode 100644 index 0000000000..0a0ea9af57 --- /dev/null +++ b/packages/links/src/cardano/module.ts @@ -0,0 +1,143 @@ +import {Links} from '@yoroi/types' +import { + LinksCardanoUriConfig, + LinksCardanoClaimV1, + LinksCardanoLegacyTransfer, +} from './types' +import {preapareParams} from './params' +import {isArray} from '@yoroi/common' +import {isCardanoAddress} from './helpers' + +export const cardanoScheme: Links.Scheme = 'web+cardano' +export const configCardanoClaimV1: Readonly = { + scheme: cardanoScheme, + authority: 'claim', + version: 'v1', + rules: { + requiredParams: ['code', 'faucet_url'], + optionalParams: [], + forbiddenParams: ['address'], + extraParams: 'include', + }, +} as const + +export const configCardanoLegacyTransfer: Readonly = + { + scheme: cardanoScheme, + authority: '', + version: '', + rules: { + requiredParams: ['address'], + optionalParams: ['amount', 'memo', 'message'], + forbiddenParams: [], + extraParams: 'drop', + }, + } as const + +export const linksCardanoModuleMaker = + (): Links.Module => { + // NOTE: asking for the config is leaky, since is part of impl and its requesting it, later with more flavors it can add a facade for parse and proxy for create + const create = ({ + config, + params, + }: { + config: LinksCardanoUriConfig + params: Links.Link['params'] + }) => { + const sanitizedParams = preapareParams({config, params}) + let url: URL + + const addSearchParams = ( + urlToAdd: URL, + paramsToAdd: Record, + ) => { + Object.entries(paramsToAdd).forEach(([key, value]) => { + // TODO: add support for records + if (isArray(value)) { + value.forEach((arrayValue) => + urlToAdd.searchParams.append(key, String(arrayValue)), + ) + } else { + urlToAdd.searchParams.append(key, value) + } + }) + } + + // legacy transfer + if (config.authority === '') { + const {address, ...restParams} = sanitizedParams + // address for legacy needs to be validated here + if (!isCardanoAddress(address)) { + throw new Links.Errors.ParamsValidationFailed( + `The param address on ${config.scheme} ${config.authority} ${config.version} must be a cardano address`, + ) + } + url = new URL(config.scheme + ':' + address) + addSearchParams(url, restParams) + } else { + url = new URL(config.scheme + '://' + config.authority + '/') + addSearchParams(url, sanitizedParams) + url.pathname = config.version + } + + return { + config, + params: sanitizedParams, + link: url.href, + } as const + } + + const parse = (text: string) => { + const url = new URL(text) + const isCardano = url.protocol.startsWith(`${cardanoScheme}:`) + if (!isCardano) return undefined + + let config: LinksCardanoUriConfig | undefined + const params: Record = {} + + url.searchParams.forEach((value, key) => { + // TODO: add support for records + if (params[key]) { + if (isArray(params[key])) { + params[key].push(value) + } else { + params[key] = [params[key], value] + } + } else { + params[key] = value + } + }) + + // NOTE: order matters + if (isCardanoClaimV1(url)) { + config = configCardanoClaimV1 + } else if (url.pathname !== '' && url.hostname === '') { + // legacy transfer address is the authority but should be handled as a param + if (!isCardanoAddress(url.pathname)) + throw new Links.Errors.ParamsValidationFailed( + `The param address is an invalid cardano address`, + ) + params.address = url.pathname + // amount is transformed to number here + if (params.amount) params.amount = Number(params.amount) + config = configCardanoLegacyTransfer + } + + if (!config) throw new Links.Errors.UnsupportedAuthority() + + return create({config, params}) + } + + return { + create, + parse, + } as const + } + +const isCardanoClaimV1 = (url: URL) => { + if (url.hostname === configCardanoClaimV1.authority) { + if (url.pathname === `/${configCardanoClaimV1.version}`) return true + throw new Links.Errors.UnsupportedVersion() + } + return false +} diff --git a/packages/links/src/cardano/params.ts b/packages/links/src/cardano/params.ts new file mode 100644 index 0000000000..9f8d052887 --- /dev/null +++ b/packages/links/src/cardano/params.ts @@ -0,0 +1,156 @@ +import {Links, Writable} from '@yoroi/types' +import {LinksCardanoUriConfig} from './types' +import {isArrayOfString, isString, isUrl} from '@yoroi/common' + +/** + * Prepares and validates parameters for a Cardano URI link based on a given configuration. + * + * This function takes a configuration object and a set of parameters, then performs several + * operations including validation, checking for forbidden and required parameters, and handling + * optional and extra parameters as per the configuration rules. + * + * It first checks for any forbidden parameters and throws an error if any are found. + * It then ensures all required parameters are present, throwing an error if any are missing. + * Optional parameters are validated if they are present. + * Extra parameters are either dropped or retained based on the `extraParams` configuration. + * + * Finally, the function returns a frozen object containing the sanitized and validated parameters. + * + * @param {object} args - The arguments object. + * @param {LinksCardanoUriConfig} args.config - The configuration object defining rules for parameters. + * @param {Links.Link['params']} args.params - The parameters to be prepared and validated. + * + * @returns {Readonly['params']>} A frozen object containing the sanitized and validated parameters. + * + * @throws {Links.Errors.ForbiddenParamsProvided} If any forbidden parameters are provided. + * @throws {Links.Errors.RequiredParamsMissing} If any required parameters are missing. + * + * @note maybe it can become part of config (.rules.validator: ({key, value}: {string, any}) => void) + */ +export const preapareParams = ({ + config, + params, +}: { + config: LinksCardanoUriConfig + params: Links.Link['params'] +}) => { + const {forbiddenParams, requiredParams, optionalParams, extraParams} = + config.rules + const paramValidator = getParamValidator(config) + const paramEntries = new Map(Object.entries(params)) + const allParams = new Set([ + ...forbiddenParams, + ...requiredParams, + ...optionalParams, + ]) + + // drop extra params + if (extraParams === 'drop') { + paramEntries.forEach((_, key) => { + if (!allParams.has(key)) { + paramEntries.delete(key) + } + }) + } + + for (const forbidenParam of forbiddenParams) { + if (paramEntries.has(forbidenParam)) { + throw new Links.Errors.ForbiddenParamsProvided( + `Please remove the param ${forbidenParam} on ${config.scheme} ${config.authority} ${config.version}`, + ) + } + } + + for (const requiredParam of requiredParams) { + if (!paramEntries.has(requiredParam)) { + throw new Links.Errors.RequiredParamsMissing( + `Please include the param ${requiredParam} on ${config.scheme} ${config.authority} ${config.version}`, + ) + } + paramValidator({ + key: requiredParam, + value: paramEntries.get(requiredParam), + }) + } + + for (const optionalParam of optionalParams) { + if (paramEntries.has(optionalParam)) { + paramValidator({ + key: optionalParam, + value: paramEntries.get(optionalParam), + }) + } + } + + return Object.freeze( + Array.from(paramEntries).reduce((sanitizedParams, [key, value]) => { + sanitizedParams[key] = value + return sanitizedParams + }, {} as Writable['params']>), // safe since is a subset of params + ) +} + +/** + * Creates a parameter validator function based on a given configuration. + * + * This function takes a `LinksCardanoUriConfig` object and returns a new function + * that is used for validating key-value pairs. The returned function takes two arguments, + * `key` and `value`, representing the parameter to be validated. The validation logic + * is determined by the structure and rules defined in the `LinksCardanoUriConfig`. + * + * If the validation fails, the validator function throws a `Links.Errors.ParamsValidationFailed` error. + * By default, parameters are validated by name. To apply specific validation logic based on the authority, + * a specific case can be added in the switch statement for that key; otherwise, the default validation for that name will be used, + * this can affect your data if `optionalParams` since `extraParams = include` are not tested against the validator. + * + * @param {LinksCardanoUriConfig} config - The configuration object defining validation rules. + * + * @returns {(key: string, value: string) => void} A validator function that takes a `key` and `value` + * as arguments and performs validation based on the provided configuration. If the validation + * fails, it throws a `Links.Errors.ParamsValidationFailed` error. + * + * @throws {Links.Errors.ParamsValidationFailed} If the parameter fails validation during the execution + * of the returned validator function. + * + */ +export const getParamValidator = + (config: LinksCardanoUriConfig) => + ({key, value}: {key: string; value: string}) => { + switch (key) { + case 'amount': { + if (/^\d{0,20}(\.\d{0,20})?$/.test(String(value))) break + throw new Links.Errors.ParamsValidationFailed( + `The param ${key} on ${config.scheme} ${config.authority} ${config.version} must be a number without thousand separators and using dot as decimal separator`, + ) + } + case 'address': + case 'code': { + // if other check besides `claim` authority is needed it should be added here conditionally + if (isString(value)) break + throw new Links.Errors.ParamsValidationFailed( + `The param ${key} on ${config.scheme} ${config.authority} ${config.version} must be a string`, + ) + } + case 'faucet_url': { + if (isUrl(value)) break + throw new Links.Errors.ParamsValidationFailed( + `The param ${key} on ${config.scheme} ${config.authority} ${config.version} must be a valid url`, + ) + } + case 'memo': { + if (isString(value) && value.length <= 255) break + throw new Links.Errors.ParamsValidationFailed( + `The param ${key} on ${config.scheme} ${config.authority} ${config.version} must be a string with max 255 chars`, + ) + } + // NOTE: we encode array: item=[1,2,3] item=1&item=2&item=3 + case 'message': { + if (isString(value) && value.length <= 64) break + if (isArrayOfString(value) && !value.some((str) => str.length > 64)) + break + throw new Links.Errors.ParamsValidationFailed( + `The param ${key} on ${config.scheme} ${config.authority} ${config.version} must be a string or array of strings with max 64 chars`, + ) + } + } + } diff --git a/packages/links/src/cardano/types.ts b/packages/links/src/cardano/types.ts new file mode 100644 index 0000000000..4c3237e412 --- /dev/null +++ b/packages/links/src/cardano/types.ts @@ -0,0 +1,31 @@ +import {Links} from '@yoroi/types' + +// CIP99 - v1 +export interface LinksCardanoClaimV1 extends Links.UriConfig { + readonly scheme: 'web+cardano' + readonly authority: 'claim' + readonly version: 'v1' + readonly rules: { + readonly requiredParams: Readonly<['code', 'faucet_url']> + readonly optionalParams: Readonly<[]> + readonly forbiddenParams: Readonly<['address']> + readonly extraParams: 'include' + } +} + +// CIP13 - initial version +export interface LinksCardanoLegacyTransfer extends Links.UriConfig { + readonly scheme: 'web+cardano' + readonly authority: '' // is the wallet address + readonly version: '' // unsupported + readonly rules: { + readonly requiredParams: Readonly<['address']> + readonly optionalParams: Readonly<['amount', 'memo', 'message']> // message - it must be str max 54 chars/array of it + readonly forbiddenParams: Readonly<[]> + readonly extraParams: 'drop' + } +} + +export type LinksCardanoUriConfig = + | LinksCardanoClaimV1 + | LinksCardanoLegacyTransfer diff --git a/packages/links/src/index.ts b/packages/links/src/index.ts new file mode 100644 index 0000000000..4668dc5600 --- /dev/null +++ b/packages/links/src/index.ts @@ -0,0 +1,2 @@ +export * from './cardano/module' +export * from './cardano/types' diff --git a/packages/links/tsconfig.build.json b/packages/links/tsconfig.build.json new file mode 100644 index 0000000000..1382480b0c --- /dev/null +++ b/packages/links/tsconfig.build.json @@ -0,0 +1,5 @@ + +{ + "extends": "./tsconfig", + "exclude": ["example", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/links/tsconfig.json b/packages/links/tsconfig.json new file mode 100644 index 0000000000..a074f3ce47 --- /dev/null +++ b/packages/links/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "declaration": true, + "baseUrl": "./src", + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext" + } +} diff --git a/packages/swap/.gitignore b/packages/swap/.gitignore index 75356714f9..32c6d7db77 100644 --- a/packages/swap/.gitignore +++ b/packages/swap/.gitignore @@ -7,6 +7,7 @@ # VSCode .vscode/ +!.vscode/launch.json jsconfig.json # Xcode diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 5a28c86d31..b00682f483 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -24,6 +24,16 @@ import {NumberLocale} from './intl/numbers' import {SwapAggregator} from './swap/aggregator' import {AppApi} from './app/api' import {AppFrontendFeesResponse, AppFrontendFeeTier} from './app/frontend-fees' +import {LinksLink, LinksModule, LinksUriConfig} from './links/link' +import { + LinksErrorExtraParamsDenied, + LinksErrorForbiddenParamsProvided, + LinksErrorParamsValidationFailed, + LinksErrorRequiredParamsMissing, + LinksErrorSchemeNotImplemented, + LinksErrorUnsupportedAuthority, + LinksErrorUnsupportedVersion, +} from './links/errors' export namespace App { export interface Storage extends AppStorage {} @@ -76,6 +86,28 @@ export namespace Balance { export type Amounts = BalanceAmounts } +export namespace Links { + export interface UriConfig extends LinksUriConfig {} + export type Scheme = LinksUriConfig['scheme'] + export type Authority = LinksUriConfig['authority'] + export type Version = LinksUriConfig['version'] + export type Rules = LinksUriConfig['rules'] + + export type Link = LinksLink + + export type Module = LinksModule + + export namespace Errors { + export class ExtraParamsDenied extends LinksErrorExtraParamsDenied {} + export class ForbiddenParamsProvided extends LinksErrorForbiddenParamsProvided {} + export class RequiredParamsMissing extends LinksErrorRequiredParamsMissing {} + export class ParamsValidationFailed extends LinksErrorParamsValidationFailed {} + export class UnsupportedAuthority extends LinksErrorUnsupportedAuthority {} + export class UnsupportedVersion extends LinksErrorUnsupportedVersion {} + export class SchemeNotImplemented extends LinksErrorSchemeNotImplemented {} + } +} + export namespace Numbers { export type Locale = NumberLocale } diff --git a/packages/types/src/links/errors.ts b/packages/types/src/links/errors.ts new file mode 100644 index 0000000000..be2f2ab5b5 --- /dev/null +++ b/packages/types/src/links/errors.ts @@ -0,0 +1,7 @@ +export class LinksErrorForbiddenParamsProvided extends Error {} +export class LinksErrorExtraParamsDenied extends Error {} +export class LinksErrorRequiredParamsMissing extends Error {} +export class LinksErrorParamsValidationFailed extends Error {} +export class LinksErrorUnsupportedAuthority extends Error {} +export class LinksErrorUnsupportedVersion extends Error {} +export class LinksErrorSchemeNotImplemented extends Error {} diff --git a/packages/types/src/links/link.ts b/packages/types/src/links/link.ts new file mode 100644 index 0000000000..2ae830501a --- /dev/null +++ b/packages/types/src/links/link.ts @@ -0,0 +1,24 @@ +export interface LinksUriConfig { + readonly scheme: 'web+cardano' + readonly authority: '' | 'transfer' | 'claim' + readonly version: 'v1' | '' + readonly rules: { + readonly requiredParams: ReadonlyArray + readonly optionalParams: ReadonlyArray + readonly forbiddenParams: ReadonlyArray + readonly extraParams: 'include' | 'deny' | 'drop' + } +} + +export type LinksParams = Readonly> + +export type LinksLink = Readonly<{ + config: T + params: LinksParams + link: string +}> + +export type LinksModule = Readonly<{ + create: (args: {config: T; params: LinksParams}) => LinksLink + parse: (text: string) => LinksLink | undefined +}> diff --git a/yoroi.code-workspace b/yoroi.code-workspace index 4cb4a49178..43f310e9df 100644 --- a/yoroi.code-workspace +++ b/yoroi.code-workspace @@ -12,6 +12,10 @@ "name": "@yoroi/swap", "path": "./packages/swap" }, + { + "name": "@yoroi/links", + "path": "./packages/links" + }, { "name": "@yoroi/banxa", "path": "./packages/banxa" From 4156f37deb35af2f297c1f3f46512ba2e6abb658 Mon Sep 17 00:00:00 2001 From: Juliano Lazzarotto <30806844+stackchain@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:34:11 +0000 Subject: [PATCH 21/39] feature(scan): interpret crypto links (#2916) --- apps/wallet-mobile/.gitignore | 3 + .../.storybook/storybook.requires.js | 7 +- apps/wallet-mobile/package.json | 3 + .../scripts/build-testing-codes.js | 68 ++++ .../src/TxHistory/TxHistoryNavigator.tsx | 83 +++- .../ImportReadOnlyWalletScreen.tsx | 4 +- apps/wallet-mobile/src/WalletNavigator.tsx | 8 +- .../CameraCodeScanner.stories.tsx} | 8 +- .../CameraCodeScanner.tsx} | 173 ++++---- .../src/components/CameraCodeScanner/index.ts | 1 + .../src/components/QRCodeScanner/index.ts | 1 - apps/wallet-mobile/src/components/index.ts | 2 +- .../Scan/common/CodeScannerButton.stories.tsx | 22 ++ .../Scan/common/CodeScannerButton.tsx | 25 ++ .../CameraPermissionDeniedIlustration.tsx | 63 +++ .../src/features/Scan/common/mocks.ts | 33 ++ .../src/features/Scan/common/parsers.test.ts | 44 +++ .../src/features/Scan/common/parsers.ts | 45 +++ .../src/features/Scan/common/types.ts | 28 ++ .../src/features/Scan/common/useDialogs.tsx | 52 +++ .../features/Scan/common/useNavigateTo.tsx | 14 + .../Scan/common/useScanErrorResolver.tsx | 22 ++ .../src/features/Scan/common/useStrings.tsx | 138 +++++++ .../Scan/common/useTriggerScanAction.tsx | 56 +++ .../Scan/useCases/ScanCodeScreen.stories.tsx | 8 + .../features/Scan/useCases/ScanCodeScreen.tsx | 67 ++++ .../OpenDeviceAppSettingsButton.stories.tsx | 12 + .../OpenDeviceAppSettingsButton.tsx | 10 + ...owCameraPermissionDeniedScreen.stories.tsx | 8 + .../ShowCameraPermissionDeniedScreen.tsx | 72 ++++ .../src/features/Send/common/navigation.ts | 2 +- .../src/features/Send/common/strings.ts | 1 + .../ReadQRCodeScreen.stories.tsx | 21 - .../InputReceiver/ReadQRCodeScreen.tsx | 51 --- .../wallet-mobile/src/i18n/locales/en-US.json | 22 ++ apps/wallet-mobile/src/navigation.tsx | 202 +++++----- apps/wallet-mobile/src/utils/feedback.ts | 7 + .../src/TxHistory/TxHistoryNavigator.json | 127 +++--- .../ImportReadOnlyWalletScreen.json | 16 +- .../messages/src/WalletNavigator.json | 72 ++-- .../src/features/Scan/common/strings.json | 17 + .../src/features/Scan/common/useStrings.json | 347 +++++++++++++++++ .../src/features/Send/common/strings.json | 368 +++++++++--------- .../common/strings.json | 17 + metro.config.js | 2 +- 45 files changed, 1779 insertions(+), 573 deletions(-) create mode 100755 apps/wallet-mobile/scripts/build-testing-codes.js rename apps/wallet-mobile/src/components/{QRCodeScanner/QRCodeScanner.stories.tsx => CameraCodeScanner/CameraCodeScanner.stories.tsx} (87%) rename apps/wallet-mobile/src/components/{QRCodeScanner/QRCodeScanner.tsx => CameraCodeScanner/CameraCodeScanner.tsx} (66%) create mode 100644 apps/wallet-mobile/src/components/CameraCodeScanner/index.ts delete mode 100644 apps/wallet-mobile/src/components/QRCodeScanner/index.ts create mode 100644 apps/wallet-mobile/src/features/Scan/common/CodeScannerButton.stories.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/CodeScannerButton.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/illustrations/CameraPermissionDeniedIlustration.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/mocks.ts create mode 100644 apps/wallet-mobile/src/features/Scan/common/parsers.test.ts create mode 100644 apps/wallet-mobile/src/features/Scan/common/parsers.ts create mode 100644 apps/wallet-mobile/src/features/Scan/common/types.ts create mode 100644 apps/wallet-mobile/src/features/Scan/common/useDialogs.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/useNavigateTo.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/useScanErrorResolver.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/useStrings.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/common/useTriggerScanAction.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ScanCodeScreen.stories.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ScanCodeScreen.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/OpenDeviceAppSettingsButton.stories.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/OpenDeviceAppSettingsButton.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/ShowCameraPermissionDeniedScreen.stories.tsx create mode 100644 apps/wallet-mobile/src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/ShowCameraPermissionDeniedScreen.tsx delete mode 100644 apps/wallet-mobile/src/features/Send/useCases/StartMultiTokenTx/InputReceiver/ReadQRCodeScreen.stories.tsx delete mode 100644 apps/wallet-mobile/src/features/Send/useCases/StartMultiTokenTx/InputReceiver/ReadQRCodeScreen.tsx create mode 100644 apps/wallet-mobile/src/utils/feedback.ts create mode 100644 apps/wallet-mobile/translations/messages/src/features/Scan/common/strings.json create mode 100644 apps/wallet-mobile/translations/messages/src/features/Scan/common/useStrings.json create mode 100644 apps/wallet-mobile/translations/messages/src/features/StartActionFromQRCodeScanning/common/strings.json diff --git a/apps/wallet-mobile/.gitignore b/apps/wallet-mobile/.gitignore index a7988f4b8f..4429355712 100644 --- a/apps/wallet-mobile/.gitignore +++ b/apps/wallet-mobile/.gitignore @@ -84,3 +84,6 @@ coverage/ # Dependency cruiser dependency-graph.svg + +# Scripts +test-codes/ diff --git a/apps/wallet-mobile/.storybook/storybook.requires.js b/apps/wallet-mobile/.storybook/storybook.requires.js index 87759f52f5..84ff74a253 100644 --- a/apps/wallet-mobile/.storybook/storybook.requires.js +++ b/apps/wallet-mobile/.storybook/storybook.requires.js @@ -71,6 +71,7 @@ const getStories = () => { "./src/components/BlueCheckbox/BlueCheckbox.stories.tsx": require("../src/components/BlueCheckbox/BlueCheckbox.stories.tsx"), "./src/components/Boundary/Boundary.stories.tsx": require("../src/components/Boundary/Boundary.stories.tsx"), "./src/components/Button/Button.stories.tsx": require("../src/components/Button/Button.stories.tsx"), + "./src/components/CameraCodeScanner/CameraCodeScanner.stories.tsx": require("../src/components/CameraCodeScanner/CameraCodeScanner.stories.tsx"), "./src/components/Checkbox/Checkbox.stories.tsx": require("../src/components/Checkbox/Checkbox.stories.tsx"), "./src/components/ConfirmTx/ConfirmTx.stories.tsx": require("../src/components/ConfirmTx/ConfirmTx.stories.tsx"), "./src/components/ConfirmTx/Dialog.stories.tsx": require("../src/components/ConfirmTx/Dialog.stories.tsx"), @@ -88,7 +89,6 @@ const getStories = () => { "./src/components/NftImageGallery/NftImageGallery.stories.tsx": require("../src/components/NftImageGallery/NftImageGallery.stories.tsx"), "./src/components/NftPreview/NftPreview.stories.tsx": require("../src/components/NftPreview/NftPreview.stories.tsx"), "./src/components/PairedBalance/PairedBalance.stories.tsx": require("../src/components/PairedBalance/PairedBalance.stories.tsx"), - "./src/components/QRCodeScanner/QRCodeScanner.stories.tsx": require("../src/components/QRCodeScanner/QRCodeScanner.stories.tsx"), "./src/components/StandardModal/StandardModal.stories.tsx": require("../src/components/StandardModal/StandardModal.stories.tsx"), "./src/components/TextInput/TextInput.stories.tsx": require("../src/components/TextInput/TextInput.stories.tsx"), "./src/components/TokenIcon/ModeratedNftIcon.stories.tsx": require("../src/components/TokenIcon/ModeratedNftIcon.stories.tsx"), @@ -106,6 +106,10 @@ const getStories = () => { "./src/features/Initialization/LanguagePickerScreen/LanguagePickerScreen.stories.tsx": require("../src/features/Initialization/LanguagePickerScreen/LanguagePickerScreen.stories.tsx"), "./src/features/Initialization/TermsOfServiceChangedScreen/TermsOfServiceChangedScreen.stories.tsx": require("../src/features/Initialization/TermsOfServiceChangedScreen/TermsOfServiceChangedScreen.stories.tsx"), "./src/features/Menu/Menu.stories.tsx": require("../src/features/Menu/Menu.stories.tsx"), + "./src/features/Scan/common/CodeScannerButton.stories.tsx": require("../src/features/Scan/common/CodeScannerButton.stories.tsx"), + "./src/features/Scan/useCases/ScanCodeScreen.stories.tsx": require("../src/features/Scan/useCases/ScanCodeScreen.stories.tsx"), + "./src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/OpenDeviceAppSettingsButton.stories.tsx": require("../src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/OpenDeviceAppSettingsButton.stories.tsx"), + "./src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/ShowCameraPermissionDeniedScreen.stories.tsx": require("../src/features/Scan/useCases/ShowCameraPermissionDeniedScreen/ShowCameraPermissionDeniedScreen.stories.tsx"), "./src/features/Send/useCases/ConfirmTx/ConfirmTxScreen.stories.tsx": require("../src/features/Send/useCases/ConfirmTx/ConfirmTxScreen.stories.tsx"), "./src/features/Send/useCases/ConfirmTx/FailedTx/FailedTxScreen.stories.tsx": require("../src/features/Send/useCases/ConfirmTx/FailedTx/FailedTxScreen.stories.tsx"), "./src/features/Send/useCases/ConfirmTx/SubmittedTx/SubmittedTxScreen.stories.tsx": require("../src/features/Send/useCases/ConfirmTx/SubmittedTx/SubmittedTxScreen.stories.tsx"), @@ -114,7 +118,6 @@ const getStories = () => { "./src/features/Send/useCases/ListAmountsToSend/AddToken/Show/MaxAmountsPerTx.stories.tsx": require("../src/features/Send/useCases/ListAmountsToSend/AddToken/Show/MaxAmountsPerTx.stories.tsx"), "./src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.stories.tsx": require("../src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.stories.tsx"), "./src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.stories.tsx": require("../src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.stories.tsx"), - "./src/features/Send/useCases/StartMultiTokenTx/InputReceiver/ReadQRCodeScreen.stories.tsx": require("../src/features/Send/useCases/StartMultiTokenTx/InputReceiver/ReadQRCodeScreen.stories.tsx"), "./src/features/Send/useCases/StartMultiTokenTx/StartMultiTokenTxScreen.stories.tsx": require("../src/features/Send/useCases/StartMultiTokenTx/StartMultiTokenTxScreen.stories.tsx"), "./src/features/Settings/About/About.stories.tsx": require("../src/features/Settings/About/About.stories.tsx"), "./src/features/Settings/ApplicationSettings/ApplicationSettingsScreen.stories.tsx": require("../src/features/Settings/ApplicationSettings/ApplicationSettingsScreen.stories.tsx"), diff --git a/apps/wallet-mobile/package.json b/apps/wallet-mobile/package.json index f703f4e9af..b2d695ea3e 100644 --- a/apps/wallet-mobile/package.json +++ b/apps/wallet-mobile/package.json @@ -6,6 +6,7 @@ "android-bundle": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res", "build": "yarn lint && yarn tsc && yarn test", "dgraph": "depcruise src --include-only \"^src\" --output-type dot | dot -T svg > dependency-graph.svg", + "generate:barcodes": "node scripts/build-testing-codes.sh", "i18n:missed": "i18n-unused display-missed", "i18n:unused": "i18n-unused display-unused", "lint": "yarn lint:typescript && yarn lint:pretty", @@ -59,6 +60,7 @@ "collectCoverage": true, "collectCoverageFrom": [ "src/**/*.{js,jsx,ts,tsx}", + "!src/**/*.stories.{js,jsx,ts,tsx}", "!src/**/*.d.ts" ], "coverageReporters": [ @@ -122,6 +124,7 @@ "@yoroi/api": "1.3.0", "@yoroi/banxa": "1.3.0", "@yoroi/common": "1.3.0", + "@yoroi/links": "1.3.0", "@yoroi/swap": "1.3.0", "add": "2.0.6", "assert": "^2.0.0", diff --git a/apps/wallet-mobile/scripts/build-testing-codes.js b/apps/wallet-mobile/scripts/build-testing-codes.js new file mode 100755 index 0000000000..3a52f70663 --- /dev/null +++ b/apps/wallet-mobile/scripts/build-testing-codes.js @@ -0,0 +1,68 @@ +const {execSync} = require('child_process') +const path = require('path') + +const codeContent = { + noLink: { + error: { + invalid: 'invalid receiver', + }, + success: { + address: + 'addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km', + domain: '$stackchain', + }, + }, + // TODO: should come from links package + links: { + error: { + schemeNotImplemented: + 'bitcoin:1BoatSLRHtKNngkdXEeobR76b53LETtpyT?amount=0.01&label=JohnDoe&message=Payment%20for%20services', + paramsValidationFailed: + 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?amount=1,23&memo=%25%24-_%2F.memo&message=%25%24-_%2F.messagei', + forbiddenParamsProvided: + 'web+cardano://claim/v1?code=42&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1', + unsupportedVersion: + 'web+cardano://claim/v2?code=42&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1', + unsupportedAuthority: + 'web+cardano://authority/v2?code=42&faucet_url=https%3A%2F%2Ffaucet.com&memo=1&message=1&amount=-%2CNaN&address=1', + }, + success: { + cardanoCip99ClaimV1: + 'web+cardano://claim/v1?code=42&faucet_url=https%3A%2F%2Ffaucet.com&memo=memo-text&message=message1&message=message2&message=message3&extra=extra', + legacyCip13Transfer: + 'web+cardano:addr_test1qrgpjmyy8zk9nuza24a0f4e7mgp9gd6h3uayp0rqnjnkl54v4dlyj0kwfs0x4e38a7047lymzp37tx0y42glslcdtzhqzp57km?extra=extra&amount=1.23&memo=memo&message=message', + }, + }, +} + +function shellEscape(str) { + return str.replace(/([\$`"\\])/g, '\\$1') +} + +const generateQRCode = (content, filename) => { + const cmd = `qrencode -o "${filename}_qr.png" "${shellEscape(content)}"` + console.log(cmd) + execSync(cmd) +} + +const generatePDF417Code= (content, filename) => { + const cmd = `zint -b 55 -o "${filename}_pdf417.png" -d "${shellEscape(content)}"` + console.log(cmd) + execSync(cmd) +} + +const processObject = (obj, prefix = '') => { + for (const key in obj) { + if (!obj.hasOwnProperty(key)) continue + const value = obj[key] + const filename = path.join(__dirname, '../test-codes', `${prefix}${key}`) + if (typeof value === 'string') { + generateQRCode(value, filename) + generatePDF417Code(value, filename); + } else if (typeof value === 'object') { + processObject(value, `${prefix}${key}_`) + } + } +} + +processObject(codeContent) diff --git a/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx b/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx index 959df49229..8c2407a465 100644 --- a/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx +++ b/apps/wallet-mobile/src/TxHistory/TxHistoryNavigator.tsx @@ -1,3 +1,4 @@ +import {useNavigation} from '@react-navigation/native' import {createStackNavigator} from '@react-navigation/stack' import { milkTokenId, @@ -10,9 +11,12 @@ import { import {Swap} from '@yoroi/types' import React from 'react' import {defineMessages, useIntl} from 'react-intl' -import {StyleSheet, Text, TouchableOpacity, TouchableOpacityProps} from 'react-native' +import {StyleSheet, Text, TouchableOpacity, TouchableOpacityProps, View, ViewProps} from 'react-native' -import {Boundary, Icon} from '../components' +import {Boundary, Icon, Spacer} from '../components' +import {CodeScannerButton} from '../features/Scan/common/CodeScannerButton' +import {ScanCodeScreen} from '../features/Scan/useCases/ScanCodeScreen' +import {ShowCameraPermissionDeniedScreen} from '../features/Scan/useCases/ShowCameraPermissionDeniedScreen/ShowCameraPermissionDeniedScreen' import {SendProvider} from '../features/Send/common/SendContext' import {ConfirmTxScreen} from '../features/Send/useCases/ConfirmTx/ConfirmTxScreen' import {FailedTxScreen} from '../features/Send/useCases/ConfirmTx/FailedTx/FailedTxScreen' @@ -20,7 +24,6 @@ import {SubmittedTxScreen} from '../features/Send/useCases/ConfirmTx/SubmittedTx import {ListAmountsToSendScreen} from '../features/Send/useCases/ListAmountsToSend' import {SelectTokenFromListScreen} from '../features/Send/useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen' import {EditAmountScreen} from '../features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen' -import {ReadQRCodeScreen} from '../features/Send/useCases/StartMultiTokenTx/InputReceiver/ReadQRCodeScreen' import {StartMultiTokenTxScreen} from '../features/Send/useCases/StartMultiTokenTx/StartMultiTokenTxScreen' import {SwapFormProvider} from '../features/Swap/common/SwapFormProvider' import {SwapTabNavigator} from '../features/Swap/SwapNavigator' @@ -33,7 +36,13 @@ import { } from '../features/Swap/useCases' import {SelectBuyTokenFromListScreen} from '../features/Swap/useCases/StartSwapScreen/CreateOrder/EditBuyAmount/SelectBuyTokenFromListScreen/SelectBuyTokenFromListScreen' import {SelectSellTokenFromListScreen} from '../features/Swap/useCases/StartSwapScreen/CreateOrder/EditSellAmount/SelectSellTokenFromListScreen/SelectSellTokenFromListScreen' -import {BackButton, defaultStackNavigationOptions, TxHistoryRoutes, useWalletNavigation} from '../navigation' +import { + BackButton, + defaultStackNavigationOptions, + TxHistoryRouteNavigation, + TxHistoryRoutes, + useWalletNavigation, +} from '../navigation' import {ReceiveScreen} from '../Receive/ReceiveScreen' import {useSelectedWallet} from '../SelectedWallet' import {COLORS} from '../theme' @@ -71,6 +80,9 @@ export const TxHistoryNavigator = () => { return swapManagerMaker({swapStorage, swapApi, frontendFeeTiers, aggregator, aggregatorTokenId}) }, [wallet.networkId, wallet.primaryTokenInfo.id, stakingKey, frontendFees, aggregatorTokenId]) + // navigator components + const headerRightHistory = React.useCallback(() => , []) + return ( @@ -88,7 +100,7 @@ export const TxHistoryNavigator = () => { component={TxHistory} options={{ title: walletName ?? '', - headerRight: () => , + headerRight: headerRightHistory, }} /> @@ -231,18 +243,6 @@ export const TxHistoryNavigator = () => { )} - , - }} - /> - { component={FailedTxScreen} options={{headerShown: false, gestureEnabled: false}} /> + + , + }} + /> + + @@ -334,6 +355,10 @@ const messages = defineMessages({ id: 'global.confirmationTransaction', defaultMessage: '!!!Confirm transaction', }, + scanTitle: { + id: 'scan.title', + defaultMessage: '!!!Please scan a QR code', + }, }) const useStrings = () => { @@ -354,6 +379,7 @@ const useStrings = () => { editAmountTitle: intl.formatMessage(messages.editAmountTitle), listAmountsToSendTitle: intl.formatMessage(messages.listAmountsToSendTitle), confirmationTransaction: intl.formatMessage(messages.confirmationTransaction), + scanTitle: intl.formatMessage(messages.scanTitle), } } @@ -373,11 +399,28 @@ const SettingsIconButton = (props: TouchableOpacityProps) => { ) } -const HeaderRightHistory = () => { +const HeaderRightHistory = React.memo(() => { const {navigateToSettings} = useWalletNavigation() + const navigation = useNavigation() - return navigateToSettings()} /> -} + return ( + + navigation.navigate('scan-start', {insideFeature: 'scan'})} + color={COLORS.ACTION_GRAY} + /> + + + + + + ) +}) +const Row = ({children, style, ...rest}: ViewProps) => ( + + {children} + +) const styles = StyleSheet.create({ receiveInfoText: { diff --git a/apps/wallet-mobile/src/WalletInit/ImportReadOnlyWallet/ImportReadOnlyWalletScreen.tsx b/apps/wallet-mobile/src/WalletInit/ImportReadOnlyWallet/ImportReadOnlyWalletScreen.tsx index 3e56aae46a..741ac1db8a 100644 --- a/apps/wallet-mobile/src/WalletInit/ImportReadOnlyWallet/ImportReadOnlyWalletScreen.tsx +++ b/apps/wallet-mobile/src/WalletInit/ImportReadOnlyWallet/ImportReadOnlyWalletScreen.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import {defineMessages, useIntl} from 'react-intl' import {ScrollView, StatusBar, StyleSheet, View} from 'react-native' -import {BulletPointItem, QRCodeScanner, Spacer, Text} from '../../components' +import {BulletPointItem, CameraCodeScanner, Spacer, Text} from '../../components' import {showErrorDialog} from '../../dialogs' import {errorMessages} from '../../i18n/global-messages' import {Logger} from '../../legacy/logging' @@ -41,7 +41,7 @@ export const ImportReadOnlyWalletScreen = () => { - + diff --git a/apps/wallet-mobile/src/WalletNavigator.tsx b/apps/wallet-mobile/src/WalletNavigator.tsx index 08afc26528..51032ee34a 100644 --- a/apps/wallet-mobile/src/WalletNavigator.tsx +++ b/apps/wallet-mobile/src/WalletNavigator.tsx @@ -5,7 +5,7 @@ import React from 'react' import {defineMessages, useIntl} from 'react-intl' import {Keyboard, Platform} from 'react-native' -import {VotingRegistration as VotingRegistration} from './Catalyst' +import {VotingRegistration} from './Catalyst' import {Icon, OfflineBanner} from './components' import {DashboardNavigator} from './Dashboard' import {MenuNavigator} from './features/Menu' @@ -74,7 +74,11 @@ const WalletTabNavigator = () => { ), tabBarLabel: strings.walletTabBarLabel, tabBarTestID: 'walletTabBarButton', - tabBarStyle: getFocusedRouteNameFromRoute(route.route) === 'send-read-qr-code' ? {display: 'none'} : {}, + tabBarStyle: + getFocusedRouteNameFromRoute(route.route) === 'send-read-qr-code' || + getFocusedRouteNameFromRoute(route.route)?.startsWith('scan-') + ? {display: 'none'} + : {}, })} > {() => ( diff --git a/apps/wallet-mobile/src/components/QRCodeScanner/QRCodeScanner.stories.tsx b/apps/wallet-mobile/src/components/CameraCodeScanner/CameraCodeScanner.stories.tsx similarity index 87% rename from apps/wallet-mobile/src/components/QRCodeScanner/QRCodeScanner.stories.tsx rename to apps/wallet-mobile/src/components/CameraCodeScanner/CameraCodeScanner.stories.tsx index 3865a7037c..2b7894b14b 100644 --- a/apps/wallet-mobile/src/components/QRCodeScanner/QRCodeScanner.stories.tsx +++ b/apps/wallet-mobile/src/components/CameraCodeScanner/CameraCodeScanner.stories.tsx @@ -6,7 +6,7 @@ import {Text, View} from 'react-native' import {Button} from '../Button' import {Spacer} from '../Spacer' -import {QRCodeScanner} from './QRCodeScanner' +import {CameraCodeScanner} from './CameraCodeScanner' storiesOf('QRCodeScanner', module).add('Default', () => ) @@ -14,7 +14,7 @@ const Wrapper = () => { const [status] = Camera.useCameraPermissions() const [publicKeyHex, setPublicKeyHex] = React.useState(null) const [path, setPath] = React.useState(null) - const [withMask, setMaskEnabled] = React.useState(true) + const [withMask, setWithMask] = React.useState(true) const handleOnRead = async ({data}) => { const parsedData = JSON.parse(data) @@ -28,7 +28,7 @@ const Wrapper = () => { return ( - + @@ -56,7 +56,7 @@ const Wrapper = () => { -