mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-21 23:48:00 +00:00
Initial commit: Pezkuwi Wallet Android
Security hardened release: - Code obfuscation enabled (minifyEnabled=true, shrinkResources=true) - Sensitive files excluded (google-services.json, keystores) - Branch.io key moved to BuildConfig placeholder - Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77 - Comprehensive ProGuard rules for crypto wallet - Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[{*.kt, *.kts}]
|
||||
#Ktlint configuration
|
||||
max_line_length = 160
|
||||
insert_final_newline = true
|
||||
indent_size = 4
|
||||
ktlint_disabled_rules = import-ordering, package-name, trailing-comma-on-call-site, trailing-comma-on-declaration-site, filename
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime, timezone
|
||||
import requests
|
||||
|
||||
|
||||
ALLOWED_SEVERITIES = {"Major", "Critical", "Normal"}
|
||||
|
||||
# Matches: "Release severity: <value>" (case-insensitive, flexible spaces)
|
||||
SEVERITY_LINE_RE = re.compile(r"^release\s+severity\s*:\s*(.+)$", re.IGNORECASE)
|
||||
|
||||
|
||||
def parse_base_params(comment_link: str) -> None:
|
||||
if not comment_link:
|
||||
print("COMMENT_LINK is not set. Provide a valid PR comment API URL in env var COMMENT_LINK.")
|
||||
sys.exit(1)
|
||||
|
||||
env_file = os.getenv("GITHUB_ENV")
|
||||
if not env_file:
|
||||
print("GITHUB_ENV is not set. This script expects GitHub Actions environment.")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
resp = requests.get(comment_link, timeout=10)
|
||||
resp.raise_for_status()
|
||||
payload = resp.json()
|
||||
except requests.RequestException as e:
|
||||
print(f"Failed to fetch PR comment: {e}")
|
||||
sys.exit(1)
|
||||
except ValueError:
|
||||
print("Response is not valid JSON.")
|
||||
sys.exit(1)
|
||||
|
||||
body = payload.get("body")
|
||||
if not isinstance(body, str) or not body.strip():
|
||||
print("PR comment body is empty. Add 'Release severity: Major | Critical | Normal'.")
|
||||
sys.exit(1)
|
||||
|
||||
lines = [line.strip() for line in body.splitlines()]
|
||||
|
||||
severity_raw = ""
|
||||
|
||||
for line in lines:
|
||||
m = SEVERITY_LINE_RE.match(line)
|
||||
if m:
|
||||
severity_raw = m.group(1).strip()
|
||||
break
|
||||
|
||||
if not severity_raw:
|
||||
print("Release severity is missing. Add a line 'Release severity: Major | Critical | Normal'.")
|
||||
sys.exit(1)
|
||||
|
||||
if severity_raw not in ALLOWED_SEVERITIES:
|
||||
print(f"Invalid severity '{severity_raw}'. Allowed values: Major, Critical, Normal.")
|
||||
sys.exit(1)
|
||||
|
||||
severity = severity_raw
|
||||
|
||||
time_iso = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
try:
|
||||
with open(env_file, "a", encoding="utf-8") as f:
|
||||
f.write(f"TIME={time_iso}\n")
|
||||
f.write(f"SEVERITY={severity}\n")
|
||||
except OSError as e:
|
||||
print(f"Failed to write to GITHUB_ENV: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse_base_params(os.getenv("COMMENT_LINK"))
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
adb devices
|
||||
|
||||
# Install debug app
|
||||
adb -s emulator-5554 install app/debug/app-debug.apk
|
||||
|
||||
# Install instrumental tests
|
||||
adb -s emulator-5554 install app/androidTest/debug/app-debug-androidTest.apk
|
||||
|
||||
# Run tests
|
||||
adb logcat -c &&
|
||||
python - <<END
|
||||
import os
|
||||
import re
|
||||
import subprocess as sp
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
done = False
|
||||
def update():
|
||||
# prevent CI from killing the process for inactivity
|
||||
while not done:
|
||||
time.sleep(5)
|
||||
print ("Running...")
|
||||
t = threading.Thread(target=update)
|
||||
t.dameon = True
|
||||
t.start()
|
||||
def run():
|
||||
os.system('adb wait-for-device')
|
||||
p = sp.Popen('adb shell am instrument -w -m -e debug false -e class "io.novafoundation.nova.balances.BalancesIntegrationTest" io.novafoundation.nova.debug.test/io.qameta.allure.android.runners.AllureAndroidJUnitRunner',
|
||||
shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
|
||||
return p.communicate()
|
||||
success = re.compile(r'OK \(\d+ tests\)')
|
||||
stdout, stderr = run()
|
||||
stdout = stdout.decode('ISO-8859-1')
|
||||
stderr = stderr.decode('ISO-8859-1')
|
||||
done = True
|
||||
print (stderr)
|
||||
print (stdout)
|
||||
if success.search(stderr + stdout):
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1) # make sure we fail if the tests fail
|
||||
END
|
||||
EXIT_CODE=$?
|
||||
adb logcat -d '*:E'
|
||||
|
||||
# Export results
|
||||
adb exec-out run-as io.novafoundation.nova.debug sh -c 'cd /data/data/io.novafoundation.nova.debug/files && tar cf - allure-results' > allure-results.tar
|
||||
|
||||
exit $EXIT_CODE
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
adb devices
|
||||
|
||||
# Install debug app
|
||||
adb -s emulator-5554 install app/debug/app-debug.apk
|
||||
|
||||
# Install instrumental tests
|
||||
adb -s emulator-5554 install test-app/androidTest/debug/app-debug-androidTest.apk
|
||||
|
||||
# Run tests
|
||||
adb logcat -c &&
|
||||
python - <<END
|
||||
import os
|
||||
import re
|
||||
import subprocess as sp
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
done = False
|
||||
def update():
|
||||
# prevent CI from killing the process for inactivity
|
||||
while not done:
|
||||
time.sleep(5)
|
||||
print ("Running...")
|
||||
t = threading.Thread(target=update)
|
||||
t.dameon = True
|
||||
t.start()
|
||||
def run():
|
||||
os.system('adb wait-for-device')
|
||||
p = sp.Popen('adb shell am instrument -w -m -e notClass io.novafoundation.nova.balances.BalancesIntegrationTest -e package io.novafoundation.nova.debug io.novafoundation.nova.debug.test/io.qameta.allure.android.runners.AllureAndroidJUnitRunner',
|
||||
shell=True, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
|
||||
return p.communicate()
|
||||
success = re.compile(r'OK \(\d+ tests\)')
|
||||
stdout, stderr = run()
|
||||
stdout = stdout.decode('ISO-8859-1')
|
||||
stderr = stderr.decode('ISO-8859-1')
|
||||
done = True
|
||||
print (stderr)
|
||||
print (stdout)
|
||||
if success.search(stderr + stdout):
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1) # make sure we fail if the tests fail
|
||||
END
|
||||
EXIT_CODE=$?
|
||||
adb logcat -d '*:E'
|
||||
|
||||
# Export results
|
||||
adb exec-out run-as io.novafoundation.nova.debug sh -c 'cd /data/data/io.novafoundation.nova.debug/files && tar cf - allure-results' > allure-results.tar
|
||||
|
||||
exit $EXIT_CODE
|
||||
@@ -0,0 +1,204 @@
|
||||
name: Reusable workflow for build Android
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
branch:
|
||||
required: true
|
||||
default: main
|
||||
type: string
|
||||
gradlew-command:
|
||||
required: false
|
||||
type: string
|
||||
default: "false"
|
||||
run-tests:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
keystore-file-name:
|
||||
required: false
|
||||
type: string
|
||||
default: "false"
|
||||
keystore-file-base64:
|
||||
required: false
|
||||
type: string
|
||||
default: "false"
|
||||
upload-name:
|
||||
required: false
|
||||
type: string
|
||||
default: "apk"
|
||||
build-debug-tests:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
# Crowdloan secrets - NOT NEEDED for Pezkuwi (own blockchain)
|
||||
ACALA_PROD_AUTH_TOKEN:
|
||||
required: false
|
||||
ACALA_TEST_AUTH_TOKEN:
|
||||
required: false
|
||||
MOONBEAM_PROD_AUTH_TOKEN:
|
||||
required: false
|
||||
MOONBEAM_TEST_AUTH_TOKEN:
|
||||
required: false
|
||||
# Fiat on-ramp - OPTIONAL (future update)
|
||||
MOONPAY_PRODUCTION_SECRET:
|
||||
required: false
|
||||
MOONPAY_TEST_SECRET:
|
||||
required: false
|
||||
# EVM chain support - REQUIRED for cross-chain
|
||||
EHTERSCAN_API_KEY_MOONBEAM:
|
||||
required: true
|
||||
EHTERSCAN_API_KEY_MOONRIVER:
|
||||
required: true
|
||||
EHTERSCAN_API_KEY_ETHEREUM:
|
||||
required: true
|
||||
INFURA_API_KEY:
|
||||
required: true
|
||||
# RPC provider - use own nodes or Dwellir
|
||||
DWELLIR_API_KEY:
|
||||
required: false
|
||||
# WalletConnect - REQUIRED for dApp connections
|
||||
WALLET_CONNECT_PROJECT_ID:
|
||||
required: true
|
||||
# Google OAuth - REQUIRED for cloud backup
|
||||
DEBUG_GOOGLE_OAUTH_ID:
|
||||
required: true
|
||||
RELEASE_GOOGLE_OAUTH_ID:
|
||||
required: true
|
||||
# Special secrets for signing:
|
||||
CI_MARKET_KEYSTORE_PASS:
|
||||
required: false
|
||||
CI_MARKET_KEYSTORE_KEY_ALIAS:
|
||||
required: false
|
||||
CI_MARKET_KEYSTORE_KEY_PASS:
|
||||
required: false
|
||||
CI_MARKET_KEY_FILE:
|
||||
required: false
|
||||
CI_KEYSTORE_PASS:
|
||||
required: false
|
||||
CI_KEYSTORE_KEY_ALIAS:
|
||||
required: false
|
||||
CI_KEYSTORE_KEY_PASS:
|
||||
required: false
|
||||
CI_GITHUB_KEYSTORE_PASS:
|
||||
required: false
|
||||
CI_GITHUB_KEYSTORE_KEY_ALIAS:
|
||||
required: false
|
||||
CI_GITHUB_KEYSTORE_KEY_PASS:
|
||||
required: false
|
||||
# Secrets for google-services:
|
||||
CI_DEVELOP_GOOGLE_SERVICES:
|
||||
required: true
|
||||
CI_PRODUCTION_GOOGLE_SERVICES:
|
||||
required: true
|
||||
|
||||
env:
|
||||
ACALA_PROD_AUTH_TOKEN: ${{ secrets.ACALA_PROD_AUTH_TOKEN }}
|
||||
ACALA_TEST_AUTH_TOKEN: ${{ secrets.ACALA_TEST_AUTH_TOKEN }}
|
||||
MOONBEAM_PROD_AUTH_TOKEN: ${{ secrets.MOONBEAM_PROD_AUTH_TOKEN }}
|
||||
MOONBEAM_TEST_AUTH_TOKEN: ${{ secrets.MOONBEAM_TEST_AUTH_TOKEN }}
|
||||
MOONPAY_PRODUCTION_SECRET: ${{ secrets.MOONPAY_PRODUCTION_SECRET }}
|
||||
MOONPAY_TEST_SECRET: ${{ secrets.MOONPAY_TEST_SECRET }}
|
||||
MERCURYO_PRODUCTION_SECRET: ${{ secrets.MERCURYO_PRODUCTION_SECRET }}
|
||||
MERCURYO_TEST_SECRET: ${{ secrets.MERCURYO_TEST_SECRET }}
|
||||
EHTERSCAN_API_KEY_MOONBEAM: ${{ secrets.EHTERSCAN_API_KEY_MOONBEAM }}
|
||||
EHTERSCAN_API_KEY_MOONRIVER: ${{ secrets.EHTERSCAN_API_KEY_MOONRIVER }}
|
||||
EHTERSCAN_API_KEY_ETHEREUM: ${{ secrets.EHTERSCAN_API_KEY_ETHEREUM }}
|
||||
INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }}
|
||||
DWELLIR_API_KEY: ${{ secrets.DWELLIR_API_KEY }}
|
||||
WALLET_CONNECT_PROJECT_ID: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}
|
||||
DEBUG_GOOGLE_OAUTH_ID: ${{ secrets.DEBUG_GOOGLE_OAUTH_ID }}
|
||||
RELEASE_GOOGLE_OAUTH_ID: ${{ secrets.RELEASE_GOOGLE_OAUTH_ID }}
|
||||
|
||||
CI_MARKET_KEYSTORE_PASS: ${{ secrets.CI_MARKET_KEYSTORE_PASS }}
|
||||
CI_MARKET_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_MARKET_KEYSTORE_KEY_ALIAS }}
|
||||
CI_MARKET_KEYSTORE_KEY_PASS: ${{ secrets.CI_MARKET_KEYSTORE_KEY_PASS }}
|
||||
CI_MARKET_KEY_FILE: ${{ secrets.RELEASE_MARKET_KEY_FILE }}
|
||||
|
||||
CI_KEYSTORE_PASS: ${{ secrets.CI_KEYSTORE_PASS }}
|
||||
CI_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_KEYSTORE_KEY_ALIAS }}
|
||||
CI_KEYSTORE_KEY_PASS: ${{ secrets.CI_KEYSTORE_KEY_PASS }}
|
||||
|
||||
CI_GITHUB_KEYSTORE_PASS: ${{ secrets.CI_GITHUB_KEYSTORE_PASS }}
|
||||
CI_GITHUB_KEYSTORE_KEY_ALIAS: ${{ secrets.CI_GITHUB_KEYSTORE_KEY_ALIAS }}
|
||||
CI_GITHUB_KEYSTORE_KEY_PASS: ${{ secrets.CI_GITHUB_KEYSTORE_KEY_PASS }}
|
||||
CI_GITHUB_KEYSTORE_KEY_FILE: ${{ secrets.BASE64_GITHUB_KEYSTORE_FILE }}
|
||||
|
||||
CI_DEVELOP_GOOGLE_SERVICES_FILE: ${{ secrets.CI_DEVELOP_GOOGLE_SERVICES }}
|
||||
CI_PRODUCTION_GOOGLE_SERVICES_FILE: ${{ secrets.CI_PRODUCTION_GOOGLE_SERVICES }}
|
||||
|
||||
POLKASSEMBLY_SUMMARY_API_KEY: ${{ secrets.POLKASSEMBLY_SUMMARY_API_KEY }}
|
||||
|
||||
CI_BUILD_ID: ${{ github.run_number }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{inputs.upload-name}}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-app:
|
||||
name: Build app and test
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout particular branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: 📂 Set up DEV Google Services
|
||||
uses: davidSchuppa/base64Secret-toFile-action@v3
|
||||
with:
|
||||
secret: ${{ env.CI_DEVELOP_GOOGLE_SERVICES_FILE }}
|
||||
filename: google-services.json
|
||||
destination-path: ./app/
|
||||
|
||||
- name: 📂 Set up PROD Google Services
|
||||
uses: davidSchuppa/base64Secret-toFile-action@v3
|
||||
with:
|
||||
secret: ${{ env.CI_PRODUCTION_GOOGLE_SERVICES_FILE }}
|
||||
filename: google-services.json
|
||||
destination-path: ./app/src/release/
|
||||
|
||||
- name: 🔧 Install dependencies
|
||||
uses: ./.github/workflows/install/
|
||||
|
||||
- name: 🧪 Run tests
|
||||
if: ${{ inputs.run-tests }}
|
||||
run: ./gradlew runTest
|
||||
|
||||
- name: 🔐 Getting github sign key
|
||||
if: ${{ startsWith(inputs.keystore-file-name, 'github_key.jks') }}
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: ${{ inputs.keystore-file-name }}
|
||||
fileDir: './app/'
|
||||
encodedString: ${{ env.CI_GITHUB_KEYSTORE_KEY_FILE }}
|
||||
|
||||
- name: 🔐 Getting market sign key
|
||||
if: ${{ startsWith(inputs.keystore-file-name, 'market_key.jks') }}
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: ${{ inputs.keystore-file-name }}
|
||||
fileDir: './app/'
|
||||
encodedString: ${{ env.CI_MARKET_KEY_FILE }}
|
||||
|
||||
- name: 🏗 Build app
|
||||
if: ${{ !startsWith(inputs.gradlew-command, 'false') }}
|
||||
run: ./gradlew ${{ inputs.gradlew-command }}
|
||||
|
||||
- name: 🏗 Build debug tests
|
||||
if: ${{ inputs.build-debug-tests }}
|
||||
run: ./gradlew assembleDebugAndroidTest
|
||||
|
||||
- name: 🧹 Delete key after building
|
||||
if: ${{ !startsWith(inputs.keystore-file-name, 'false') }}
|
||||
run: rm ./app/${{ inputs.keystore-file-name }}
|
||||
|
||||
- name: ➡️ Upload build artifacts
|
||||
if: ${{ !startsWith(inputs.gradlew-command, 'false') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.upload-name }}
|
||||
path: app/build/outputs/apk/
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Appium Mobile Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
app_url:
|
||||
type: string
|
||||
description: URL to download the app from
|
||||
required: true
|
||||
test_grep:
|
||||
type: string
|
||||
description: Test pattern to run (pytest marker or test name)
|
||||
required: false
|
||||
default: "android"
|
||||
allure_job_run_id:
|
||||
type: string
|
||||
description: ALLURE_JOB_RUN_ID service parameter. Leave blank.
|
||||
required: false
|
||||
default: ""
|
||||
allure_username:
|
||||
type: string
|
||||
description: ALLURE_USERNAME service parameter. Leave blank.
|
||||
required: false
|
||||
default: ""
|
||||
secrets:
|
||||
WORKFLOW_TOKEN:
|
||||
required: true
|
||||
ALLURE_TOKEN:
|
||||
required: false
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: '3.9'
|
||||
CI: true
|
||||
ALLURE_ENDPOINT: https://pezkuwi.testops.cloud/
|
||||
ALLURE_PROJECT_ID: 103
|
||||
|
||||
jobs:
|
||||
trigger-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger mobile tests in test repository
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
script: |
|
||||
const response = await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'pezkuwichain',
|
||||
repo: 'appium-mobile-tests',
|
||||
workflow_id: 'browserstack-tests.yml',
|
||||
ref: 'master',
|
||||
inputs: {
|
||||
app_url: '${{ inputs.app_url }}',
|
||||
ALLURE_JOB_RUN_ID: '${{ inputs.allure_job_run_id }}',
|
||||
ALLURE_USERNAME: '${{ inputs.allure_username }}'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Mobile tests triggered successfully');
|
||||
console.log('App URL:', '${{ inputs.app_url }}');
|
||||
|
||||
- name: Wait for test completion (optional)
|
||||
if: false
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
script: |
|
||||
console.log('Waiting for test completion...');
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
name: Run balances tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 */8 * * *'
|
||||
|
||||
jobs:
|
||||
build-app:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: ${{github.head_ref}}
|
||||
gradlew-command: assembleDebug
|
||||
upload-name: develop-apk
|
||||
run-tests: false
|
||||
build-debug-tests: true
|
||||
secrets: inherit
|
||||
|
||||
run-tests:
|
||||
needs: [build-app]
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: develop-apk
|
||||
path: app
|
||||
|
||||
- name: Debug path
|
||||
run: |
|
||||
ls -laR app
|
||||
|
||||
- name: Add permissions
|
||||
run: chmod +x .github/scripts/run_balances_test.sh
|
||||
|
||||
- name: Run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
disable-animations: true
|
||||
profile: Nexus 6
|
||||
api-level: 29
|
||||
script: .github/scripts/run_balances_test.sh
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: anroid-results
|
||||
path: ./allure-results.tar
|
||||
|
||||
report:
|
||||
needs: [run-tests]
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Unzip results
|
||||
run: |
|
||||
find artifacts -name allure-results.tar -exec tar -xvf {} \;
|
||||
|
||||
- name: Debug path
|
||||
run: |
|
||||
ls -laR
|
||||
|
||||
- name: Generate report
|
||||
uses: ./.github/workflows/report/
|
||||
with:
|
||||
token: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||
keep-reports-history: 30
|
||||
|
||||
telegram-notification:
|
||||
needs: [report]
|
||||
runs-on: ubuntu-latest
|
||||
if: failure()
|
||||
steps:
|
||||
- name: Notify Telegram channel
|
||||
uses: appleboy/telegram-action@master
|
||||
with:
|
||||
to: ${{ secrets.TELEGRAM_TO }}
|
||||
token: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
format: html
|
||||
message: |
|
||||
💸 Balances tests failed.
|
||||
|
||||
Test Results: https://pezkuwichain.github.io/balances_test_result/${{ github.run_number }}/index.html
|
||||
|
||||
Github run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Distribute app to Play Store
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
app_version:
|
||||
description: 'Version of application'
|
||||
required: true
|
||||
default: v*.*.*
|
||||
branch:
|
||||
description: 'From which branch the application will be built'
|
||||
required: true
|
||||
default: main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: ${{ github.event.inputs.branch }}
|
||||
gradlew-command: assembleReleaseMarket
|
||||
keystore-file-name: market_key.jks
|
||||
secrets: inherit
|
||||
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Environment Variables
|
||||
uses: tw3lveparsecs/github-actions-setvars@v0.1
|
||||
with:
|
||||
envFilePath: .github/workflows/variables/android.env
|
||||
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: app
|
||||
|
||||
- name: Rename artifacts
|
||||
run: mv app/releaseMarket/app-releaseMarket.apk app/releaseMarket/pezkuwi-wallet-android-${{ github.event.inputs.app_version }}.apk
|
||||
|
||||
- name: Market publication
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }} # The contents of your service-account.json
|
||||
packageName: io.pezkuwichain.wallet
|
||||
releaseFiles: app/releaseMarket/pezkuwi-wallet-android-${{ github.event.inputs.app_version }}.apk
|
||||
track: production # One of production, beta, alpha, internalsharing, internal, or a custom track name (case sensitive)
|
||||
status: draft # One of "completed", "inProgress", "halted", "draft"
|
||||
inAppUpdatePriority: 2
|
||||
userFraction: 1.0 # Percentage of users who should get the staged version of the app. Defaults to 1.0
|
||||
whatsNewDirectory: distribution/whatsnew # The directory of localized "whats new" files to upload as the release notes. The files contained in the whatsNewDirectory MUST use the pattern whatsnew-<LOCALE> where LOCALE is using the BCP 47 format
|
||||
mappingFile: app/build/outputs/mapping/release/mapping.txt # The mapping.txt file used to de-obfuscate your stack traces from crash reports
|
||||
debugSymbols: app/intermediates/merged_native_libs/release/out/lib
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Install dependencies for Android build
|
||||
description: Contains all dependencies for Android build
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: ☕️ Install Java
|
||||
uses: actions/setup-java@v4.0.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
cmdline-tools-version: 12266719
|
||||
|
||||
- name: Install NDK
|
||||
run: echo "y" | sudo ${ANDROID_SDK_ROOT}/cmdline-tools/16.0/bin/sdkmanager --install "ndk;26.1.10909125" --sdk_root=${ANDROID_SDK_ROOT}
|
||||
shell: bash
|
||||
|
||||
- name: Set ndk.dir in local.properties
|
||||
run: echo "ndk.dir=${{ steps.setup-ndk.outputs.ndk-path }}" >> local.properties
|
||||
shell: bash
|
||||
|
||||
- name: 🦀 Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Add targets
|
||||
run: |
|
||||
rustup target add armv7-linux-androideabi
|
||||
rustup target add i686-linux-android
|
||||
rustup target add x86_64-linux-android
|
||||
rustup target add aarch64-linux-android
|
||||
shell: bash
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Manual Firebase distribution
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
firebase_group:
|
||||
description: 'Firebase group'
|
||||
required: true
|
||||
default: dev-team
|
||||
branch:
|
||||
description: 'From which branch the application will be built'
|
||||
required: true
|
||||
default: main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: ${{ github.event.inputs.branch }}
|
||||
gradlew-command: assembleDevelop
|
||||
secrets: inherit
|
||||
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: app
|
||||
|
||||
- name: 🗳 Upload to Firebase
|
||||
uses: ./.github/workflows/upload-to-firebase
|
||||
with:
|
||||
appId: ${{ secrets.ANDROID_DEVELOP_FIREBASE_APP_ID }}
|
||||
firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
|
||||
releaseNotes: ${{ github.event.head_commit.message }}
|
||||
test-groups: ${{ github.event.inputs.firebase_group }}
|
||||
upload-file: app/develop/app-develop.apk
|
||||
@@ -0,0 +1,76 @@
|
||||
name: PR Workflow
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
pull_request_review_comment:
|
||||
types: [created, edited, deleted]
|
||||
|
||||
jobs:
|
||||
checkRef:
|
||||
if: github.event.pull_request.base.ref == 'master' || github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
is_rc: ${{ steps.check_ref.outputs.ref_contains_rc }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check if "rc" or "hotfix" is present in github.ref
|
||||
id: check_ref
|
||||
run: |
|
||||
echo ${{ github.head_ref || github.ref_name }}
|
||||
if [[ "${{ github.head_ref || github.ref_name }}" == "rc/"* || "${{ github.head_ref || github.ref_name }}" == "hotfix/"* ]]; then
|
||||
echo "ref_contains_rc=1" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "ref_contains_rc=0" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Output check result
|
||||
run: |
|
||||
echo "Output: ${{ steps.check_ref.outputs.ref_contains_rc }}"
|
||||
|
||||
make-or-update-pr:
|
||||
runs-on: ubuntu-latest
|
||||
permissions: write-all
|
||||
needs: checkRef
|
||||
if: needs.checkRef.outputs.is_rc == '1'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-includes: Release notes
|
||||
|
||||
- name: Create comment link
|
||||
id: create_link
|
||||
run: |
|
||||
echo "COMMENT_LINK=https://api.github.com/repos/${{ github.repository }}/issues/comments/${{ steps.fc.outputs.comment-id }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Extract version from branch name
|
||||
id: extract_version
|
||||
run: |
|
||||
VERSION=${{ github.head_ref }}
|
||||
VERSION=${VERSION/hotfix/rc} # Replace "hotfix" with "rc"
|
||||
echo "version=${VERSION#*rc/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: tibdex/github-app-token@v2
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.PR_APP_ID }}
|
||||
private_key: ${{ secrets.PR_APP_TOKEN }}
|
||||
|
||||
- name: Run Python script
|
||||
run: python .github/scripts/pr_comment_extract_data.py
|
||||
|
||||
- name: Create new branch and file in pezkuwi-wallet-android-releases repo
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
repository: pezkuwichain/pezkuwi-wallet-android-releases
|
||||
event-type: create-pr
|
||||
client-payload: '{"version": "${{ steps.extract_version.outputs.version }}", "comment_link": "${{ env.COMMENT_LINK }}", "time": "${{ env.TIME }}", "severity": "${{ env.SEVERITY }}"}'
|
||||
@@ -0,0 +1,88 @@
|
||||
name: Publish GitHub release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: master
|
||||
gradlew-command: assembleReleaseGithub
|
||||
keystore-file-name: github_key.jks
|
||||
secrets: inherit
|
||||
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: app
|
||||
|
||||
- name: Rename artifacts
|
||||
run: mv app/releaseGithub/app-releaseGithub.apk app/releaseGithub/pezkuwi-wallet-android-${{ github.ref_name }}-github.apk
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
name: Release ${{ github.ref_name }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
generate_release_notes: true
|
||||
draft: true
|
||||
files: app/releaseGithub/pezkuwi-wallet-android-${{ github.ref_name }}-github.apk
|
||||
|
||||
deploy-to-vps:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: deploy
|
||||
|
||||
- name: Prepare APK for VPS
|
||||
run: |
|
||||
mkdir -p ./vps-deploy
|
||||
mv deploy/releaseGithub/app-releaseGithub.apk ./vps-deploy/pezkuwi-wallet.apk
|
||||
|
||||
- name: Create version.json
|
||||
run: |
|
||||
TAG="${{ github.ref_name }}"
|
||||
VERSION="${TAG#v}"
|
||||
cat > ./vps-deploy/version.json << EOF
|
||||
{
|
||||
"version": "$VERSION",
|
||||
"tag": "$TAG",
|
||||
"apk": "pezkuwi-wallet.apk",
|
||||
"updated_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Deploy to VPS
|
||||
uses: appleboy/scp-action@v1.0.0
|
||||
with:
|
||||
host: ${{ secrets.VPS_HOST }}
|
||||
username: ${{ secrets.VPS_USER }}
|
||||
key: ${{ secrets.VPS_SSH_KEY }}
|
||||
source: 'vps-deploy/*'
|
||||
target: '/var/www/wallet.pezkuwichain.io'
|
||||
strip_components: 1
|
||||
overwrite: true
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Pull request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: ${{github.head_ref}}
|
||||
gradlew-command: assembleDevelop
|
||||
build-debug-tests: false # TODO: Enable this, when debug build will be fixed for tests
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Build test and deploy debug apk
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: pezkuwichain/pezkuwi-wallet-android/.github/workflows/android_build.yml@main
|
||||
with:
|
||||
branch: main
|
||||
gradlew-command: assembleDebug
|
||||
secrets: inherit
|
||||
|
||||
upload-to-firebase:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: app
|
||||
|
||||
- name: 🗳 Upload to Firebase
|
||||
uses: ./.github/workflows/upload-to-firebase
|
||||
with:
|
||||
appId: ${{ secrets.ANDROID_DEBUG_FIREBASE_APP_ID }}
|
||||
firebase-token: ${{ secrets.CREDENTIAL_FILE_CONTENT }}
|
||||
releaseNotes: ${{ github.event.head_commit.message }}
|
||||
test-groups: dev-team
|
||||
upload-file: app/debug/app-debug.apk
|
||||
|
||||
upload-to-s3:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
outputs:
|
||||
s3_url: ${{ steps.s3_upload.outputs.s3_url }}
|
||||
env:
|
||||
S3_BUCKET: s3://pezkuwi-wallet-android-app
|
||||
S3_REGION: nl-ams
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download built artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: apk
|
||||
path: app
|
||||
|
||||
- name: ⚙️ Upload to S3
|
||||
id: s3_upload
|
||||
uses: ./.github/workflows/upload-to-s3
|
||||
with:
|
||||
s3_region: ${{ env.S3_REGION }}
|
||||
s3_access_key: ${{ secrets.SCW_ACCESS_KEY }}
|
||||
s3_secret_key: ${{ secrets.SCW_SECRET_KEY }}
|
||||
s3_bucket: ${{ env.S3_BUCKET }}
|
||||
upload_file: app/debug/app-debug.apk
|
||||
|
||||
- name: Show S3 URL
|
||||
run: |
|
||||
echo "App uploaded to: ${{ steps.s3_upload.outputs.s3_url }}"
|
||||
|
||||
appium-mobile-tests:
|
||||
needs: [upload-to-s3]
|
||||
if: ${{ always() && needs.upload-to-s3.result == 'success' }}
|
||||
uses: ./.github/workflows/appium-mobile-tests.yml
|
||||
with:
|
||||
app_url: ${{ needs.upload-to-s3.outputs.s3_url }}
|
||||
test_grep: "android"
|
||||
allure_job_run_id: ""
|
||||
secrets:
|
||||
WORKFLOW_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
ALLURE_TOKEN: ${{ secrets.ALLURE_TOKEN }}
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Publish report to gh-pages
|
||||
description: That workflow will publish report to the github-pages
|
||||
inputs:
|
||||
keep-reports-history:
|
||||
description: "History storage depth, integer"
|
||||
required: true
|
||||
token:
|
||||
description: "Github PAT"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Get Allure history
|
||||
uses: actions/checkout@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
repository: pezkuwichain/balances_test_result
|
||||
ref: gh-pages
|
||||
path: gh-pages
|
||||
|
||||
- name: Allure Report action
|
||||
uses: simple-elf/allure-report-action@master
|
||||
if: always()
|
||||
with:
|
||||
allure_results: allure-results
|
||||
allure_history: allure-history
|
||||
keep_reports: ${{ inputs.keep-reports-history }}
|
||||
github_repo: balances_test_result
|
||||
github_repo_owner: pezkuwichain
|
||||
|
||||
- name: Deploy report to Github Pages
|
||||
if: always()
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
deploy_key: ${{ inputs.token }}
|
||||
publish_branch: gh-pages
|
||||
publish_dir: allure-history
|
||||
external_repository: pezkuwichain/balances_test_result
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Sync main and master branches
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
|
||||
- name: Sync branches
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||||
echo "main was updated, syncing master..."
|
||||
git checkout master
|
||||
git reset --hard origin/main
|
||||
git push origin master --force
|
||||
elif [ "${{ github.ref }}" = "refs/heads/master" ]; then
|
||||
echo "master was updated, syncing main..."
|
||||
git checkout main
|
||||
git reset --hard origin/master
|
||||
git push origin main --force
|
||||
fi
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Bump app version
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
['master']
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-tag:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Version in build.gradle
|
||||
run: |
|
||||
versionName=$(grep "versionName" build.gradle | grep -o "'.*'")
|
||||
versionName=${versionName//\'/}
|
||||
echo Version in gradle file: $versionName
|
||||
echo "GRADLE_APP_VERSION=$versionName" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Check if tag exists
|
||||
id: version
|
||||
run: |
|
||||
if git rev-parse "v${{ env.GRADLE_APP_VERSION }}" >/dev/null 2>&1; then
|
||||
echo "Tag already exists"
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Tag does not exist"
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: rickstaa/action-create-tag@v1
|
||||
if: steps.version.outputs.changed == 'true'
|
||||
with:
|
||||
tag: 'v${{ env.GRADLE_APP_VERSION }}'
|
||||
message: Release v${{ env.GRADLE_APP_VERSION }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,49 @@
|
||||
name: Deploy to Firebase
|
||||
description: Deploy artifacts by provided path to firebase for provided groups
|
||||
inputs:
|
||||
appId:
|
||||
description: 'Firebase AppID'
|
||||
required: true
|
||||
firebase-token:
|
||||
description: 'Token from firebase CLI'
|
||||
required: true
|
||||
releaseNotes:
|
||||
description: 'Notes which will attach to version'
|
||||
required: true
|
||||
default: 'update'
|
||||
test-groups:
|
||||
description: 'Groups which will receive the version'
|
||||
required: true
|
||||
upload-file:
|
||||
description: 'File to uploading'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Upload artifact to Firebase App Distribution
|
||||
id: upload
|
||||
continue-on-error: true
|
||||
uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
|
||||
with:
|
||||
appId: ${{ inputs.appId }}
|
||||
serviceCredentialsFileContent: ${{ inputs.firebase-token }}
|
||||
releaseNotes: ${{ inputs.releaseNotes }}
|
||||
groups: ${{ inputs.test-groups }}
|
||||
file: ${{ inputs.upload-file }}
|
||||
|
||||
- name: Sleep for 60 seconds
|
||||
uses: whatnick/wait-action@master
|
||||
if: steps.upload.outcome=='failure'
|
||||
with:
|
||||
time: '60s'
|
||||
|
||||
- name: Retry upload artifacts
|
||||
if: steps.upload.outcome=='failure'
|
||||
uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0
|
||||
with:
|
||||
appId: ${{ inputs.appId }}
|
||||
serviceCredentialsFileContent: ${{ inputs.firebase-token }}
|
||||
releaseNotes: ${{ inputs.releaseNotes }}
|
||||
groups: ${{ inputs.test-groups }}
|
||||
file: ${{ inputs.upload-file }}
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Upload to s3
|
||||
description: Upload artifacts to s3
|
||||
inputs:
|
||||
s3_region:
|
||||
description: 'S3 region'
|
||||
required: true
|
||||
s3_access_key:
|
||||
description: 'S3 access key'
|
||||
required: true
|
||||
s3_secret_key:
|
||||
description: 'S3 secret key'
|
||||
required: true
|
||||
s3_bucket:
|
||||
description: 'S3 bucket'
|
||||
required: true
|
||||
upload_file:
|
||||
description: 'File to uploading'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
s3_url:
|
||||
description: 'URL of the uploaded file'
|
||||
value: ${{ steps.interact_with_storage.outputs.s3_url }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up S3cmd cli tool
|
||||
uses: s3-actions/s3cmd@v1.6.1
|
||||
with:
|
||||
provider: scaleway
|
||||
region: ${{ inputs.s3_region }}
|
||||
secret_key: ${{ inputs.s3_secret_key }}
|
||||
access_key: ${{ inputs.s3_access_key }}
|
||||
|
||||
- name: List available S3 buckets
|
||||
run: s3cmd ls
|
||||
shell: bash
|
||||
|
||||
- name: Interact with object storage
|
||||
id: interact_with_storage
|
||||
run: |
|
||||
file="${{ inputs.upload_file }}"
|
||||
destination_s3="${{ inputs.s3_bucket }}"
|
||||
filename=$(basename "$file")
|
||||
s3cmd sync "$file" "${destination_s3}/${filename}" --acl-public
|
||||
|
||||
bucket_name=$(echo "${{ inputs.s3_bucket }}" | sed 's|s3://||')
|
||||
s3_url="https://${bucket_name}.s3.${{ inputs.s3_region }}.scw.cloud/${filename}"
|
||||
echo "s3_url=${s3_url}" >> $GITHUB_OUTPUT
|
||||
echo "Uploaded file URL: ${s3_url}"
|
||||
shell: bash
|
||||
@@ -0,0 +1,3 @@
|
||||
# Android Build Variables for Pezkuwi Wallet
|
||||
APP_NAME=Pezkuwi Wallet
|
||||
PACKAGE_NAME=io.pezkuwichain.wallet
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
.DS_Store
|
||||
/build
|
||||
*/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
app/src/main/aidl/
|
||||
app/*.apk
|
||||
/.idea/
|
||||
|
||||
# ignore jacoco coverage reports
|
||||
/coverage
|
||||
|
||||
*.jks
|
||||
.java-version
|
||||
|
||||
# ignore database schemas
|
||||
/core-db/schemas/io.novafoundation.nova.core_db.AppDatabase/*.json
|
||||
|
||||
# database schemas exceptions
|
||||
!/core-db/schemas/io.novafoundation.nova.core_db.AppDatabase/1.json
|
||||
!/core-db/schemas/io.novafoundation.nova.core_db.AppDatabase/2.json
|
||||
!/core-db/schemas/io.novafoundation.nova.core_db.AppDatabase/8.json
|
||||
!/core-db/schemas/io.novafoundation.nova.core_db.AppDatabase/9.json
|
||||
|
||||
# Firebase config - contains sensitive API keys
|
||||
google-services.json
|
||||
**/google-services.json
|
||||
|
||||
# Version properties
|
||||
version.properties
|
||||
.kotlin/
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "nova-wallet-dapp-js"]
|
||||
path = nova-wallet-dapp-js
|
||||
url = git@github.com:nova-wallet/nova-wallet-dapp-js.git
|
||||
[submodule "nova-wallet-metamask-js"]
|
||||
path = nova-wallet-metamask-js
|
||||
url = git@github.com:nova-wallet/nova-wallet-metamask-js.git
|
||||
@@ -0,0 +1,380 @@
|
||||
# PezWallet Android - Pezkuwi Uyumluluk Değişiklikleri
|
||||
|
||||
Bu dosya, Pezkuwi chain uyumluluğu için yapılan tüm değişiklikleri takip eder.
|
||||
Context sıfırlanması durumunda referans olarak kullanılmalıdır.
|
||||
|
||||
---
|
||||
|
||||
## DEBUG KODLARI (Production öncesi KALDIRILMALI)
|
||||
|
||||
### 1. FeeLoaderV2Provider.kt - Hata mesajı gösterimi
|
||||
**Dosya:** `feature-wallet-api/src/main/java/io/novafoundation/nova/feature_wallet_api/presentation/mixin/fee/v2/FeeLoaderV2Provider.kt`
|
||||
**Değişiklik:**
|
||||
```kotlin
|
||||
// ÖNCE:
|
||||
message = resourceManager.getString(R.string.choose_amount_error_fee),
|
||||
|
||||
// SONRA (DEBUG):
|
||||
message = "DEBUG: $errorMsg | Runtime: $diagnostics",
|
||||
```
|
||||
**Temizleme:**
|
||||
- `"DEBUG: $errorMsg | Runtime: $diagnostics"` → `resourceManager.getString(R.string.choose_amount_error_fee)` olarak geri al
|
||||
- `val diagnostics = try { ... }` bloğunu kaldır
|
||||
|
||||
---
|
||||
|
||||
### 2. RuntimeFactory.kt - Diagnostic değişken ve log'lar
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/multiNetwork/runtime/RuntimeFactory.kt`
|
||||
**Eklenenler:**
|
||||
```kotlin
|
||||
// Companion object içinde:
|
||||
companion object {
|
||||
@Volatile
|
||||
var lastDiagnostics: String = "not yet initialized"
|
||||
}
|
||||
|
||||
// constructRuntimeInternal içinde:
|
||||
lastDiagnostics = "typesUsage=$typesUsage, ExtrinsicSig=$hasExtrinsicSignature, MultiSig=$hasMultiSignature, typeCount=${types.size}"
|
||||
|
||||
// Log satırları:
|
||||
Log.d("RuntimeFactory", "DEBUG: TypesUsage for chain $chainId = $typesUsage")
|
||||
Log.d("RuntimeFactory", "DEBUG: Loading BASE types for $chainId")
|
||||
Log.d("RuntimeFactory", "DEBUG: BASE types loaded, hash=$baseHash, typeCount=${types.size}")
|
||||
Log.d("RuntimeFactory", "DEBUG: Chain $chainId - ExtrinsicSignature=$hasExtrinsicSignature, MultiSignature=$hasMultiSignature, typesUsage=$typesUsage, typeCount=${types.size}")
|
||||
Log.d("RuntimeFactory", "DEBUG: BaseTypes loaded, length=${baseTypesRaw.length}, contains ExtrinsicSignature=${baseTypesRaw.contains("ExtrinsicSignature")}")
|
||||
Log.e("RuntimeFactory", "DEBUG: BaseTypes NOT in cache!")
|
||||
```
|
||||
**Temizleme:**
|
||||
- `companion object { ... }` bloğunu kaldır
|
||||
- `lastDiagnostics = ...` satırını kaldır
|
||||
- Tüm `Log.d/Log.e("RuntimeFactory", "DEBUG: ...")` satırlarını kaldır
|
||||
|
||||
---
|
||||
|
||||
### 3. CustomTransactionExtensions.kt - Log satırları
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
### 4. ExtrinsicBuilderFactory.kt - Log satırları
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/ExtrinsicBuilderFactory.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
### 5. PezkuwiAddressConstructor.kt - Log satırları
|
||||
**Dosya:** `common/src/main/java/io/novafoundation/nova/common/utils/PezkuwiAddressConstructor.kt`
|
||||
**Temizlenecek:** Tüm `Log.d(TAG, ...)` satırları ve `private const val TAG` tanımı
|
||||
|
||||
---
|
||||
|
||||
### 6. RealExtrinsicService.kt - Extrinsic build hata log'u
|
||||
**Dosya:** `feature-account-impl/src/main/java/io/novafoundation/nova/feature_account_impl/data/extrinsic/RealExtrinsicService.kt`
|
||||
**Eklenen:**
|
||||
```kotlin
|
||||
val extrinsic = try {
|
||||
extrinsicBuilder.buildExtrinsic()
|
||||
} catch (e: Exception) {
|
||||
Log.e("RealExtrinsicService", "Failed to build extrinsic for chain ${chain.name}", e)
|
||||
Log.e("RealExtrinsicService", "SigningMode: $signingMode, Chain: ${chain.id}")
|
||||
throw e
|
||||
}
|
||||
```
|
||||
**Temizleme:** try-catch bloğunu kaldır, sadece `extrinsicBuilder.buildExtrinsic()` bırak
|
||||
|
||||
---
|
||||
|
||||
## FEATURE DEĞİŞİKLİKLERİ (Kalıcı)
|
||||
|
||||
### 1. PezkuwiAddressConstructor.kt - YENİ DOSYA
|
||||
**Dosya:** `common/src/main/java/io/novafoundation/nova/common/utils/PezkuwiAddressConstructor.kt`
|
||||
**Açıklama:** Pezkuwi chain'leri için özel address constructor. SDK'nın AddressInstanceConstructor'ı "Address" type'ını ararken, Pezkuwi "pezsp_runtime::multiaddress::MultiAddress" kullanıyor.
|
||||
|
||||
---
|
||||
|
||||
### 2. RuntimeSnapshotExt.kt - Address type lookup
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/util/RuntimeSnapshotExt.kt`
|
||||
**Değişiklik:** Birden fazla address type ismi deneniyor:
|
||||
```kotlin
|
||||
val addressType = typeRegistry["Address"]
|
||||
?: typeRegistry["MultiAddress"]
|
||||
?: typeRegistry["sp_runtime::multiaddress::MultiAddress"]
|
||||
?: typeRegistry["pezsp_runtime::multiaddress::MultiAddress"]
|
||||
?: return false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Signed Extension Dosyaları - YENİ DOSYALAR
|
||||
**Dosyalar:**
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/AuthorizeCall.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/CheckNonZeroSender.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/CheckWeight.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/WeightReclaim.kt`
|
||||
- `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/PezkuwiCheckMortality.kt`
|
||||
|
||||
**Açıklama:** Pezkuwi chain'leri için özel signed extension'lar
|
||||
|
||||
---
|
||||
|
||||
### 3.1. PezkuwiCheckMortality.kt - YENİ DOSYA
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/extensions/PezkuwiCheckMortality.kt`
|
||||
**Açıklama:** SDK'nın CheckMortality'si metadata type lookup yaparak encode ediyor ve Pezkuwi'de bu başarısız oluyordu ("failed to encode extension CheckMortality" hatası). Bu custom extension, Era ve blockHash'i doğrudan encode ediyor.
|
||||
**Neden gerekli:** SDK CheckMortality, Era type'ını metadata'dan arıyor. Pezkuwi metadata'sında Era type'ı `pezsp_runtime.generic.era.Era` DictEnum olarak tanımlı ve SDK bunu handle edemiyor.
|
||||
|
||||
**Kod:**
|
||||
```kotlin
|
||||
class PezkuwiCheckMortality(
|
||||
era: Era.Mortal,
|
||||
blockHash: ByteArray
|
||||
) : FixedValueTransactionExtension(
|
||||
name = "CheckMortality",
|
||||
implicit = blockHash, // blockHash goes into signer payload
|
||||
explicit = createEraEntry(era) // Era as DictEnum.Entry
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. CustomTransactionExtensions.kt - Pezkuwi extension logic
|
||||
**Dosya:** `runtime/src/main/java/io/novafoundation/nova/runtime/extrinsic/CustomTransactionExtensions.kt`
|
||||
**Değişiklik:** `isPezkuwiChain()` fonksiyonu eklendi, Pezkuwi için farklı extension'lar kullanılıyor
|
||||
|
||||
---
|
||||
|
||||
### 5. Address encoding yaklaşımı değişikliği
|
||||
**Eski yaklaşım:** `AddressInstanceConstructor` veya `PezkuwiAddressConstructor` ile type ismine göre tahmin
|
||||
**Yeni yaklaşım:** `argumentType("dest").constructAccountLookupInstance(accountId)` ile metadata'dan gerçek type alınıyor
|
||||
|
||||
**Güncellenen dosyalar:**
|
||||
1. `feature-wallet-api/.../ExtrinsicBuilderExt.kt` - **YENİ YAKLAŞIM**: metadata'dan type alıyor
|
||||
2. `feature-governance-impl/.../ExtrinsicBuilderExt.kt` - Zaten doğru yaklaşımı kullanıyordu
|
||||
3. Diğer dosyalar hala PezkuwiAddressConstructor kullanıyor (gerekirse güncellenecek):
|
||||
- `feature-staking-impl/.../ExtrinsicBuilderExt.kt`
|
||||
- `feature-staking-impl/.../NominationPoolsCalls.kt`
|
||||
- `feature-proxy-api/.../ExtrinsicBuilderExt.kt`
|
||||
- `feature-wallet-impl/.../StatemineAssetTransfers.kt`
|
||||
- `feature-wallet-impl/.../OrmlAssetTransfers.kt`
|
||||
- `feature-wallet-impl/.../NativeAssetIssuer.kt`
|
||||
- `feature-wallet-impl/.../OrmlAssetIssuer.kt`
|
||||
- `feature-wallet-impl/.../StatemineAssetIssuer.kt`
|
||||
- `feature-account-impl/.../ProxiedSigner.kt`
|
||||
|
||||
---
|
||||
|
||||
### 6. CHAINS_URL - GitHub'a yönlendirme
|
||||
**Dosya:** `runtime/build.gradle`
|
||||
**Değişiklik:**
|
||||
```gradle
|
||||
// ÖNCE:
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://wallet.pezkuwichain.io/chains.json\""
|
||||
|
||||
// SONRA:
|
||||
buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/v22/android/chains.json\""
|
||||
```
|
||||
**Neden:** wallet.pezkuwichain.io/chains.json Telegram miniapp için kullanılıyor ve `"types": null`. Android için ayrı chains.json gerekli.
|
||||
|
||||
---
|
||||
|
||||
### 7. chains/v22/android/chains.json - Android-specific chains
|
||||
**Repo:** `pezkuwi-wallet-utils`
|
||||
**Dosya:** `chains/v22/android/chains.json`
|
||||
**Açıklama:** Android uygulama için özel chains.json. wallet.pezkuwichain.io'dan kopyalandı ve şu değişiklikler yapıldı:
|
||||
- `"types": { "overridesCommon": false }` eklendi (TypesUsage.BASE için)
|
||||
- `"feeViaRuntimeCall": true` eklendi
|
||||
**Etkilenen chain'ler:**
|
||||
- Pezkuwi Mainnet (bb4a61ab0c4b8c12f5eab71d0c86c482e03a275ecdafee678dea712474d33d75)
|
||||
- Pezkuwi Asset Hub (00d0e1d0581c3cd5c5768652d52f4520184018b44f56a2ae1e0dc9d65c00c948)
|
||||
- Pezkuwi People Chain (58269e9c184f721e0309332d90cafc410df1519a5dc27a5fd9b3bf5fd2d129f8)
|
||||
- Zagros Testnet (96eb58af1bb7288115b5e4ff1590422533e749293f231974536dc6672417d06f)
|
||||
|
||||
---
|
||||
|
||||
### 8. default.json - MultiAddress inline tanımı
|
||||
**Repo:** `pezkuwi-wallet-utils`
|
||||
**Dosya:** `chains/types/default.json`
|
||||
**Değişiklik:** MultiAddress artık GenericMultiAddress'e referans vermiyor, inline enum olarak tanımlı:
|
||||
```json
|
||||
"MultiAddress": {
|
||||
"type": "enum",
|
||||
"type_mapping": [
|
||||
["Id", "AccountId"],
|
||||
["Index", "Compact<u32>"],
|
||||
["Raw", "Bytes"],
|
||||
["Address32", "H256"],
|
||||
["Address20", "H160"]
|
||||
]
|
||||
}
|
||||
```
|
||||
**Neden:** v14Preset() GenericMultiAddress içermiyor, bu yüzden type çözümlenemiyordu.
|
||||
|
||||
---
|
||||
|
||||
### 9. PezkuwiIntegrationTest.kt - YENİ DOSYA
|
||||
**Dosya:** `app/src/androidTest/java/io/novafoundation/nova/PezkuwiIntegrationTest.kt`
|
||||
**Açıklama:** Pezkuwi chain'leri için integration testleri:
|
||||
- Runtime type kontrolü (ExtrinsicSignature, MultiSignature, Address, MultiAddress)
|
||||
- ExtrinsicBuilder oluşturma
|
||||
- Transfer call yapısı kontrolü
|
||||
- Signed extensions kontrolü
|
||||
- Utility asset kontrolü
|
||||
**Çalıştırma:**
|
||||
```bash
|
||||
./gradlew :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.novafoundation.nova.PezkuwiIntegrationTest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 10. GitHub Actions - Branch senkronizasyonu
|
||||
**Dosya:** `.github/workflows/sync-branches.yml`
|
||||
**Açıklama:** main ve master branch'lerini otomatik senkronize eder.
|
||||
- main'e push → master güncellenir
|
||||
- master'a push → main güncellenir
|
||||
|
||||
---
|
||||
|
||||
### 7. pezkuwi.json - Chain-specific types (ASSETS)
|
||||
**Dosya:** `runtime/src/main/assets/types/pezkuwi.json`
|
||||
**Açıklama:** Pezkuwi chain'leri için özel type tanımları
|
||||
```json
|
||||
{
|
||||
"types": {
|
||||
"ExtrinsicSignature": "MultiSignature",
|
||||
"Address": "pezsp_runtime::multiaddress::MultiAddress",
|
||||
"LookupSource": "pezsp_runtime::multiaddress::MultiAddress"
|
||||
},
|
||||
"typesAlias": {
|
||||
"pezsp_runtime::multiaddress::MultiAddress": "MultiAddress",
|
||||
"pezsp_runtime::MultiSignature": "MultiSignature",
|
||||
"pezsp_runtime.generic.era.Era": "Era"
|
||||
}
|
||||
}
|
||||
```
|
||||
**NOT:** Bu dosya şu anda kullanılmıyor çünkü TypesUsage.BASE kullanılıyor. TypesUsage.BOTH veya OWN için chains.json'da URL eklenebilir.
|
||||
|
||||
---
|
||||
|
||||
## SORUN GEÇMİŞİ
|
||||
|
||||
1. **"Network not responding"** - Fee hesaplama hatası
|
||||
- Çözüm: feeViaRuntimeCall eklendi, custom signed extension'lar eklendi
|
||||
|
||||
2. **"IllegalStateException: Type Address was not found"** - Address type lookup hatası
|
||||
- Çözüm: RuntimeSnapshotExt.kt'de birden fazla type ismi deneniyor
|
||||
|
||||
3. **"EncodeDecodeException: is not a valid instance"** - Address encoding hatası
|
||||
- Çözüm: `argumentType("dest").constructAccountLookupInstance(accountId)` ile metadata'dan gerçek type alınıyor (ExtrinsicBuilderExt.kt)
|
||||
|
||||
4. **"failed to encode extension CheckMortality"** - CheckMortality encoding hatası
|
||||
- SDK'nın CheckMortality'si metadata type lookup yaparak Era'yı encode etmeye çalışıyor
|
||||
- Pezkuwi Era type'ı `pezsp_runtime.generic.era.Era` DictEnum olarak tanımlı
|
||||
- Çözüm: `PezkuwiCheckMortality` custom extension'ı oluşturuldu, Era'yı `DictEnum.Entry("MortalX", secondByte)` olarak veriyor
|
||||
|
||||
5. **"IllegalStateException: Type ExtrinsicSignature was not found"** - ExtrinsicSignature type hatası ✅ ÇÖZÜLDÜ
|
||||
- SDK "ExtrinsicSignature" type'ını arıyor ama Pezkuwi chain'leri `"types": null` kullanıyordu
|
||||
- `TypesUsage.NONE` olduğu için base types (default.json) yüklenmiyordu
|
||||
- **Çözüm:**
|
||||
- `runtime/build.gradle` içinde CHAINS_URL GitHub'a yönlendirildi
|
||||
- `pezkuwi-wallet-utils/chains/v22/android/chains.json` oluşturuldu (`"types": { "overridesCommon": false }`)
|
||||
- Artık `TypesUsage.BASE` kullanılıyor ve default.json yükleniyor
|
||||
|
||||
6. **"IllegalStateException: Type Address was not found"** - Address type hatası ✅ ÇÖZÜLDÜ
|
||||
- v14Preset() `GenericMultiAddress` içermiyor
|
||||
- default.json'da `"MultiAddress": "GenericMultiAddress"` tanımlıydı ama GenericMultiAddress çözümlenemiyordu
|
||||
- **Çözüm:** default.json'da MultiAddress inline enum olarak tanımlandı:
|
||||
```json
|
||||
"MultiAddress": {
|
||||
"type": "enum",
|
||||
"type_mapping": [
|
||||
["Id", "AccountId"],
|
||||
["Index", "Compact<u32>"],
|
||||
["Raw", "Bytes"],
|
||||
["Address32", "H256"],
|
||||
["Address20", "H160"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
7. **"TypeReference is null"** - Transfer onaylama hatası (DEVAM EDİYOR)
|
||||
- Fee hesaplama çalışıyor ✅
|
||||
- Transfer onaylama sırasında hata oluşuyor
|
||||
- Muhtemelen signing sırasında bir type çözümlenemiyor
|
||||
- Debug logging eklendi: `RealExtrinsicService.kt`
|
||||
- Stack trace bekleniyor
|
||||
|
||||
---
|
||||
|
||||
## ÇALIŞAN İMPLEMENTASYONLAR (Referans)
|
||||
|
||||
### 1. pezkuwi-extension (Browser Extension)
|
||||
**Konum:** `/home/mamostehp/pezkuwi-extension/`
|
||||
**Nasıl çalışıyor:**
|
||||
- `@pezkuwi/types` (polkadot.js fork) kullanıyor
|
||||
- `TypeRegistry` ile dynamic type handling
|
||||
- Custom user extensions:
|
||||
```javascript
|
||||
const PEZKUWI_USER_EXTENSIONS = {
|
||||
AuthorizeCall: {
|
||||
extrinsic: {},
|
||||
payload: {}
|
||||
}
|
||||
};
|
||||
```
|
||||
- `registry.setSignedExtensions(payload.signedExtensions, PEZKUWI_USER_EXTENSIONS)` ile extension'lar ekleniyor
|
||||
- Metadata'dan registry oluşturuluyor: `metadataExpand(metadata, false)`
|
||||
|
||||
### 2. pezkuwi-subxt (Rust)
|
||||
**Konum:** `/home/mamostehp/pezkuwi-sdk/vendor/pezkuwi-subxt/`
|
||||
**Nasıl çalışıyor:**
|
||||
- Rust'ta compile-time type generation
|
||||
- Metadata'dan otomatik type oluşturma
|
||||
|
||||
### 3. Telegram Miniapp
|
||||
- Web tabanlı, polkadot.js kullanıyor
|
||||
- `"types": null` ile çalışıyor çünkü metadata v14+ self-contained
|
||||
|
||||
---
|
||||
|
||||
## TEMİZLEME KONTROL LİSTESİ
|
||||
|
||||
Production release öncesi yapılacaklar:
|
||||
|
||||
- [ ] FeeLoaderV2Provider.kt - DEBUG mesajını ve diagnostics'i kaldır
|
||||
- [ ] RuntimeFactory.kt - companion object ve debug log'ları kaldır
|
||||
- [ ] CustomTransactionExtensions.kt - Log satırlarını kaldır
|
||||
- [ ] ExtrinsicBuilderFactory.kt - Log satırlarını kaldır
|
||||
- [ ] PezkuwiAddressConstructor.kt - Log satırlarını kaldır (varsa)
|
||||
- [ ] RealExtrinsicService.kt - try-catch debug bloğunu kaldır
|
||||
- [x] Test et: Fee hesaplama çalışıyor mu? ✅
|
||||
- [ ] Test et: Transfer işlemi çalışıyor mu? (TypeReference hatası devam ediyor)
|
||||
|
||||
---
|
||||
|
||||
## TYPE LOADING AKIŞI (Referans)
|
||||
|
||||
```
|
||||
chains.json
|
||||
↓
|
||||
"types": { "overridesCommon": false } → TypesUsage.BASE
|
||||
"types": { "url": "...", "overridesCommon": false } → TypesUsage.BOTH
|
||||
"types": { "url": "...", "overridesCommon": true } → TypesUsage.OWN
|
||||
"types": null → TypesUsage.NONE
|
||||
↓
|
||||
RuntimeFactory.constructRuntime()
|
||||
↓
|
||||
TypesUsage.BASE → constructBaseTypes() → fetch from DEFAULT_TYPES_URL
|
||||
TypesUsage.BOTH → constructBaseTypes() + constructOwnTypes()
|
||||
TypesUsage.OWN → constructOwnTypes() only
|
||||
TypesUsage.NONE → use v14Preset() only
|
||||
↓
|
||||
TypeRegistry
|
||||
↓
|
||||
RuntimeSnapshot
|
||||
```
|
||||
|
||||
**DEFAULT_TYPES_URL:** `https://raw.githubusercontent.com/pezkuwichain/pezkuwi-wallet-utils/master/chains/types/default.json`
|
||||
|
||||
---
|
||||
|
||||
*Son güncelleme: 2026-02-03 06:30 (CHAINS_URL GitHub'a yönlendirildi, MultiAddress inline tanımlandı, Integration test eklendi, TypeReference hatası araştırılıyor)*
|
||||
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,9 @@
|
||||
Nova - Polkadot, Kusama wallet
|
||||
|
||||
Copyright 2022-2025 Novasama Technologies PTE. LTD.
|
||||
This product includes software developed at Novasama Technologies PTE. LTD.
|
||||
|
||||
Some parts of this product are derived from https://github.com/soramitsu/fearless-Android, which belongs to Soramitsu K.K. and was mostly developed by our team of developers from May 1, 2020, to October 5, 2021.
|
||||
Copyright 2021, Soramitsu Helvetia AG, all rights reserved.
|
||||
|
||||
License Rights transferred from Novasama Technologies PTE. LTD to Novasama Technologies GmbH starting from 1st of April 2023
|
||||
@@ -0,0 +1,118 @@
|
||||
# Pezkuwi Wallet Android
|
||||
|
||||
Next generation mobile wallet for Pezkuwichain and the Polkadot ecosystem.
|
||||
|
||||
[](https://twitter.com/pezkuwichain)
|
||||
|
||||
## About
|
||||
|
||||
Pezkuwi Wallet is a next-generation mobile application for the Pezkuwichain and Polkadot ecosystem. It provides a transparent, community-oriented wallet experience with convenient UX/UI, fast performance, and strong security.
|
||||
|
||||
**Key Features:**
|
||||
- Full Pezkuwichain support (HEZ & PEZ tokens)
|
||||
- Full Polkadot ecosystem compatibility
|
||||
- Staking, Governance, DeFi
|
||||
- NFT support
|
||||
- Cross-chain transfers (XCM)
|
||||
- Hardware wallet support (Ledger, Polkadot Vault)
|
||||
- WalletConnect v2
|
||||
- Push notifications
|
||||
|
||||
## Native Tokens
|
||||
|
||||
| Token | Network | Description |
|
||||
|-------|---------|-------------|
|
||||
| HEZ | Relay Chain | Native token for fees and staking |
|
||||
| PEZ | Asset Hub | Governance token |
|
||||
|
||||
## Build Instructions
|
||||
|
||||
### Clone Repository
|
||||
|
||||
```bash
|
||||
git clone git@github.com:pezkuwichain/pezkuwi-wallet-android.git
|
||||
```
|
||||
|
||||
### Install NDK
|
||||
|
||||
Install NDK version `26.1.10909125` from SDK Manager:
|
||||
Tools -> SDK Manager -> SDK Tools -> NDK (Side by Side)
|
||||
|
||||
### Install Rust
|
||||
|
||||
Install Rust by following [official instructions](https://www.rust-lang.org/tools/install).
|
||||
|
||||
Add Android build targets:
|
||||
|
||||
```bash
|
||||
rustup target add armv7-linux-androideabi
|
||||
rustup target add i686-linux-android
|
||||
rustup target add x86_64-linux-android
|
||||
rustup target add aarch64-linux-android
|
||||
```
|
||||
|
||||
### Update local.properties
|
||||
|
||||
Add the following lines to your `local.properties`:
|
||||
|
||||
```properties
|
||||
ACALA_PROD_AUTH_TOKEN=mock
|
||||
ACALA_TEST_AUTH_TOKEN=mock
|
||||
CI_KEYSTORE_KEY_ALIAS=mock
|
||||
CI_KEYSTORE_KEY_PASS=mock
|
||||
CI_KEYSTORE_PASS=mock
|
||||
DEBUG_GOOGLE_OAUTH_ID=mock
|
||||
RELEASE_GOOGLE_OAUTH_ID=mock
|
||||
DWELLIR_API_KEY=mock
|
||||
EHTERSCAN_API_KEY_ETHEREUM=mock
|
||||
EHTERSCAN_API_KEY_MOONBEAM=mock
|
||||
EHTERSCAN_API_KEY_MOONRIVER=mock
|
||||
INFURA_API_KEY=mock
|
||||
MERCURYO_PRODUCTION_SECRET=mock
|
||||
MERCURYO_TEST_SECRET=mock
|
||||
MOONBEAM_PROD_AUTH_TOKEN=mock
|
||||
MOONBEAM_TEST_AUTH_TOKEN=mock
|
||||
MOONPAY_PRODUCTION_SECRET=mock
|
||||
MOONPAY_TEST_SECRET=mock
|
||||
WALLET_CONNECT_PROJECT_ID=mock
|
||||
```
|
||||
|
||||
**Note:** Firebase and Google-related features (Notifications, Cloud Backups) require proper configuration.
|
||||
|
||||
### Build Types
|
||||
|
||||
- `debug`: Uses fixed keystore for Google services
|
||||
- `debugLocal`: Uses your local debug keystore
|
||||
- `release`: Production build
|
||||
|
||||
## Supported Languages
|
||||
|
||||
- English
|
||||
- Turkish (Türkçe)
|
||||
- Kurdish Kurmanji (Kurmancî)
|
||||
- Spanish (Español)
|
||||
- French (Français)
|
||||
- German (Deutsch)
|
||||
- Russian (Русский)
|
||||
- Japanese (日本語)
|
||||
- Chinese (中文)
|
||||
- Korean (한국어)
|
||||
- Portuguese (Português)
|
||||
- Vietnamese (Tiếng Việt)
|
||||
- And more...
|
||||
|
||||
## Resources
|
||||
|
||||
- Website: https://pezkuwichain.io
|
||||
- Documentation: https://docs.pezkuwichain.io
|
||||
- Telegram: https://t.me/pezkuwichain
|
||||
- Twitter: https://twitter.com/pezkuwichain
|
||||
- GitHub: https://github.com/pezkuwichain
|
||||
|
||||
## License
|
||||
|
||||
Pezkuwi Wallet Android is available under the Apache 2.0 license. See the LICENSE file for more info.
|
||||
|
||||
Based on Nova Wallet (https://novawallet.io) - © Novasama Technologies GmbH
|
||||
|
||||
© Dijital Kurdistan Tech Institute 2026
|
||||
@@ -0,0 +1,37 @@
|
||||
def APP_MODULE = 'app'
|
||||
|
||||
subprojects {
|
||||
if (!file("$projectDir/build.gradle").exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (APP_MODULE == project.name) {
|
||||
apply plugin: 'com.android.application'
|
||||
} else {
|
||||
apply plugin: 'com.android.library'
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: "com.google.devtools.ksp"
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.minSdkVersion
|
||||
targetSdkVersion rootProject.targetSdkVersion
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||
freeCompilerArgs = ["-Xcontext-receivers"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/build
|
||||
/release*
|
||||
|
||||
src/release*/google-services.json
|
||||
!src/release/google-services.json
|
||||
@@ -0,0 +1,336 @@
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'com.google.firebase.appdistribution'
|
||||
apply plugin: "com.github.triplet.play"
|
||||
apply from: "../scripts/versions.gradle"
|
||||
apply from: "../scripts/secrets.gradle"
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
applicationId rootProject.applicationId
|
||||
versionCode computeVersionCode()
|
||||
versionName computeVersionName()
|
||||
|
||||
// Branch.io key from local.properties or environment variable
|
||||
manifestPlaceholders = [
|
||||
BRANCH_KEY: readRawSecretOrNull('BRANCH_KEY') ?: "key_test_placeholder"
|
||||
]
|
||||
}
|
||||
signingConfigs {
|
||||
dev {
|
||||
storeFile file('develop_key.jks')
|
||||
storePassword readRawSecretOrNull('CI_KEYSTORE_PASS')
|
||||
keyAlias readRawSecretOrNull('CI_KEYSTORE_KEY_ALIAS')
|
||||
keyPassword readRawSecretOrNull('CI_KEYSTORE_KEY_PASS')
|
||||
}
|
||||
market {
|
||||
storeFile file('market_key.jks')
|
||||
storePassword readRawSecretOrNull('CI_MARKET_KEYSTORE_PASS')
|
||||
keyAlias readRawSecretOrNull('CI_MARKET_KEYSTORE_KEY_ALIAS')
|
||||
keyPassword readRawSecretOrNull('CI_MARKET_KEYSTORE_KEY_PASS')
|
||||
}
|
||||
github {
|
||||
storeFile file('github_key.jks')
|
||||
storePassword readRawSecretOrNull('CI_GITHUB_KEYSTORE_PASS')
|
||||
keyAlias readRawSecretOrNull('CI_GITHUB_KEYSTORE_KEY_ALIAS')
|
||||
keyPassword readRawSecretOrNull('CI_GITHUB_KEYSTORE_KEY_PASS')
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix '-debug'
|
||||
|
||||
buildConfigField "String", "BuildType", "\"debug\""
|
||||
}
|
||||
debugLocal {
|
||||
initWith buildTypes.debug
|
||||
matchingFallbacks = ['debug']
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
|
||||
buildConfigField "String", "BuildType", "\"release\""
|
||||
}
|
||||
releaseTest {
|
||||
initWith buildTypes.release
|
||||
matchingFallbacks = ['release']
|
||||
signingConfig signingConfigs.debug
|
||||
|
||||
versionNameSuffix '-releaseTest'
|
||||
applicationIdSuffix '.releaseTest'
|
||||
|
||||
buildConfigField "String", "BuildType", "\"releaseTest\""
|
||||
}
|
||||
releaseMarket {
|
||||
initWith buildTypes.release
|
||||
matchingFallbacks = ['release']
|
||||
signingConfig signingConfigs.market
|
||||
|
||||
versionNameSuffix "-${releaseApplicationSuffix}"
|
||||
applicationIdSuffix ".${releaseApplicationSuffix}"
|
||||
|
||||
buildConfigField "String", "BuildType", "\"releaseMarket\""
|
||||
}
|
||||
releaseGithub {
|
||||
initWith buildTypes.release
|
||||
matchingFallbacks = ['release']
|
||||
signingConfig signingConfigs.github
|
||||
|
||||
buildConfigField "String", "BuildType", "\"releaseGithub\""
|
||||
}
|
||||
develop {
|
||||
signingConfig signingConfigs.dev
|
||||
matchingFallbacks = ['debug']
|
||||
versionNameSuffix '-develop'
|
||||
applicationIdSuffix '.dev'
|
||||
//Init firebase
|
||||
def localReleaseNotes = releaseNotes()
|
||||
def localFirebaseGroup = firebaseGroup()
|
||||
firebaseAppDistribution {
|
||||
releaseNotes = localReleaseNotes
|
||||
groups = localFirebaseGroup
|
||||
}
|
||||
|
||||
buildConfigField "String", "BuildType", "\"develop\""
|
||||
}
|
||||
instrumentialTest {
|
||||
initWith buildTypes.debug
|
||||
matchingFallbacks = ['debug']
|
||||
defaultConfig.testInstrumentationRunner "io.qameta.allure.android.runners.AllureAndroidJUnitRunner"
|
||||
|
||||
buildConfigField "String", "BuildType", "\"instrumentalTest\""
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
releaseGithub {
|
||||
res.srcDirs = ['src/release/res']
|
||||
}
|
||||
releaseMarket {
|
||||
res.srcDirs = ['src/release/res']
|
||||
}
|
||||
releaseTest {
|
||||
res.srcDirs = ['src/release/res']
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
language {
|
||||
enableSplit = false
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
String name = variant.buildType.name
|
||||
if (name != "release" && name.startsWith("release")) {
|
||||
createBindReleaseFileTask(variant.buildType.name)
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources.excludes.add("META-INF/versions/9/previous-compilation-data.bin")
|
||||
resources.excludes.add("META-INF/DEPENDENCIES")
|
||||
resources.excludes.add("META-INF/NOTICE.md")
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
namespace 'io.novafoundation.nova.app'
|
||||
}
|
||||
|
||||
void createBindReleaseFileTask(String destination) {
|
||||
String taskName = "bind${destination.capitalize()}GithubGoogleServicesToRelease"
|
||||
|
||||
Task task = task(taskName, type: Copy) {
|
||||
description = "Switches to RELEASE google-services.json for ${destination}"
|
||||
from "src/release"
|
||||
include "google-services.json"
|
||||
into "src/${destination}"
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
def capitalizedDestination = destination.capitalize()
|
||||
def dependentTasks = [
|
||||
"process${capitalizedDestination}GoogleServices".toString(),
|
||||
"merge${capitalizedDestination}JniLibFolders".toString(),
|
||||
"merge${capitalizedDestination}StartupProfile".toString(),
|
||||
"merge${capitalizedDestination}Shaders".toString(),
|
||||
"merge${capitalizedDestination}ArtProfile".toString()
|
||||
]
|
||||
|
||||
dependentTasks.forEach {
|
||||
tasks.getByName(it).dependsOn(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play {
|
||||
serviceAccountCredentials = file(System.env.CI_PLAY_KEY ?: "../key/fake.json")
|
||||
track = "beta"
|
||||
releaseStatus = "completed"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':core-db')
|
||||
implementation project(':common')
|
||||
implementation project(':feature-splash')
|
||||
|
||||
implementation project(':feature-onboarding-api')
|
||||
implementation project(':feature-onboarding-impl')
|
||||
|
||||
implementation project(':feature-ledger-api')
|
||||
implementation project(':feature-ledger-core')
|
||||
implementation project(':feature-ledger-impl')
|
||||
|
||||
implementation project(':feature-account-api')
|
||||
implementation project(':feature-account-impl')
|
||||
|
||||
implementation project(':feature-account-migration')
|
||||
|
||||
implementation project(':feature-wallet-api')
|
||||
implementation project(':feature-wallet-impl')
|
||||
|
||||
implementation project(':runtime')
|
||||
implementation project(':web3names')
|
||||
|
||||
implementation project(':feature-staking-api')
|
||||
implementation project(':feature-staking-impl')
|
||||
|
||||
implementation project(':feature-crowdloan-api')
|
||||
implementation project(':feature-crowdloan-impl')
|
||||
|
||||
implementation project(':feature-dapp-api')
|
||||
implementation project(':feature-dapp-impl')
|
||||
|
||||
implementation project(':feature-nft-api')
|
||||
implementation project(':feature-nft-impl')
|
||||
|
||||
implementation project(':feature-currency-api')
|
||||
implementation project(':feature-currency-impl')
|
||||
|
||||
implementation project(':feature-governance-api')
|
||||
implementation project(':feature-governance-impl')
|
||||
|
||||
implementation project(':feature-assets')
|
||||
|
||||
implementation project(':feature-vote')
|
||||
|
||||
implementation project(':feature-versions-api')
|
||||
implementation project(':feature-versions-impl')
|
||||
|
||||
implementation project(':caip')
|
||||
|
||||
implementation project(':feature-external-sign-api')
|
||||
implementation project(':feature-external-sign-impl')
|
||||
|
||||
implementation project(':feature-wallet-connect-api')
|
||||
implementation project(':feature-wallet-connect-impl')
|
||||
|
||||
implementation project(':feature-proxy-api')
|
||||
implementation project(':feature-proxy-impl')
|
||||
|
||||
implementation project(':feature-settings-api')
|
||||
implementation project(':feature-settings-impl')
|
||||
|
||||
implementation project(":feature-swap-core")
|
||||
implementation project(':feature-swap-api')
|
||||
implementation project(':feature-swap-impl')
|
||||
|
||||
implementation project(":feature-buy-api")
|
||||
implementation project(":feature-buy-impl")
|
||||
|
||||
implementation project(':feature-push-notifications')
|
||||
implementation project(':feature-deep-linking')
|
||||
|
||||
implementation project(':feature-cloud-backup-api')
|
||||
implementation project(':feature-cloud-backup-impl')
|
||||
|
||||
implementation project(':feature-banners-api')
|
||||
implementation project(':feature-banners-impl')
|
||||
|
||||
implementation project(':feature-ahm-api')
|
||||
implementation project(':feature-ahm-impl')
|
||||
|
||||
implementation project(':feature-gift-api')
|
||||
implementation project(':feature-gift-impl')
|
||||
|
||||
implementation project(':bindings:metadata_shortener')
|
||||
implementation project(':bindings:sr25519-bizinikiwi')
|
||||
|
||||
implementation project(":feature-xcm:impl")
|
||||
|
||||
implementation project(":feature-multisig:operations")
|
||||
|
||||
implementation project(':test-shared')
|
||||
|
||||
implementation kotlinDep
|
||||
|
||||
implementation biometricDep
|
||||
|
||||
|
||||
implementation androidDep
|
||||
implementation constraintDep
|
||||
|
||||
implementation zXingEmbeddedDep
|
||||
|
||||
implementation navigationFragmentDep
|
||||
implementation navigationUiDep
|
||||
|
||||
implementation roomDep
|
||||
|
||||
implementation substrateSdkDep
|
||||
|
||||
implementation daggerDep
|
||||
ksp daggerCompiler
|
||||
|
||||
implementation lifecycleDep
|
||||
ksp lifecycleCompiler
|
||||
|
||||
implementation lifeCycleKtxDep
|
||||
|
||||
implementation retrofitDep
|
||||
implementation gsonConvertedDep
|
||||
|
||||
implementation gifDep
|
||||
|
||||
compileOnly wsDep
|
||||
|
||||
implementation coroutinesDep
|
||||
|
||||
testImplementation project(':test-shared')
|
||||
|
||||
implementation insetterDep
|
||||
|
||||
implementation liveDataKtxDep
|
||||
|
||||
implementation platform(firebaseBomDep)
|
||||
implementation firestoreDep
|
||||
implementation firebaseCloudMessagingDep
|
||||
implementation firebaseAppCheck
|
||||
|
||||
implementation walletConnectCoreDep, withoutTransitiveAndroidX
|
||||
implementation walletConnectWalletDep, withoutTransitiveAndroidX
|
||||
|
||||
kspAndroidTest daggerCompiler
|
||||
|
||||
androidTestImplementation androidTestRunnerDep
|
||||
androidTestImplementation androidTestRulesDep
|
||||
androidTestImplementation androidJunitDep
|
||||
|
||||
androidTestImplementation allureKotlinModel
|
||||
androidTestImplementation allureKotlinCommons
|
||||
androidTestImplementation allureKotlinJunit4
|
||||
androidTestImplementation allureKotlinAndroid
|
||||
}
|
||||
|
||||
task printVersion {
|
||||
doLast {
|
||||
println "versionName:${computeVersionName()}"
|
||||
}
|
||||
}
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
# ============================================================
|
||||
# Pezkuwi Wallet ProGuard Rules
|
||||
# ============================================================
|
||||
|
||||
# Keep line numbers for debugging crash reports
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
# ============================================================
|
||||
# Kotlin
|
||||
# ============================================================
|
||||
-dontwarn kotlin.**
|
||||
-keep class kotlin.Metadata { *; }
|
||||
-keepclassmembers class kotlin.Metadata {
|
||||
public <methods>;
|
||||
}
|
||||
|
||||
# Kotlin Coroutines
|
||||
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
|
||||
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
|
||||
-keepclassmembers class kotlinx.coroutines.** {
|
||||
volatile <fields>;
|
||||
}
|
||||
-dontwarn kotlinx.coroutines.**
|
||||
|
||||
# ============================================================
|
||||
# Retrofit & OkHttp
|
||||
# ============================================================
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-dontwarn javax.annotation.**
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
-keep class retrofit2.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes Exceptions
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit2.http.* <methods>;
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Gson
|
||||
# ============================================================
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-dontwarn sun.misc.**
|
||||
-keep class com.google.gson.** { *; }
|
||||
-keep class * extends com.google.gson.TypeAdapter
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
-keepclassmembers,allowobfuscation class * {
|
||||
@com.google.gson.annotations.SerializedName <fields>;
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# BouncyCastle Crypto
|
||||
# ============================================================
|
||||
-keep class org.bouncycastle.** { *; }
|
||||
-dontwarn org.bouncycastle.**
|
||||
|
||||
# ============================================================
|
||||
# Native JNI Bindings (Rust)
|
||||
# ============================================================
|
||||
# SR25519 signing
|
||||
-keep class io.novafoundation.nova.** { native <methods>; }
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# Keep all JNI related classes
|
||||
-keep class io.parity.** { *; }
|
||||
|
||||
# ============================================================
|
||||
# Substrate SDK
|
||||
# ============================================================
|
||||
-keep class jp.co.soramitsu.** { *; }
|
||||
-dontwarn jp.co.soramitsu.**
|
||||
|
||||
# ============================================================
|
||||
# Firebase
|
||||
# ============================================================
|
||||
-keep class com.google.firebase.** { *; }
|
||||
-dontwarn com.google.firebase.**
|
||||
-keep class com.google.android.gms.** { *; }
|
||||
-dontwarn com.google.android.gms.**
|
||||
|
||||
# ============================================================
|
||||
# Branch.io Deep Linking
|
||||
# ============================================================
|
||||
-keep class io.branch.** { *; }
|
||||
-dontwarn io.branch.**
|
||||
|
||||
# ============================================================
|
||||
# Web3j (Ethereum)
|
||||
# ============================================================
|
||||
-keep class org.web3j.** { *; }
|
||||
-dontwarn org.web3j.**
|
||||
|
||||
# ============================================================
|
||||
# SQLCipher
|
||||
# ============================================================
|
||||
-keep class net.sqlcipher.** { *; }
|
||||
-dontwarn net.sqlcipher.**
|
||||
|
||||
# ============================================================
|
||||
# Room Database
|
||||
# ============================================================
|
||||
-keep class * extends androidx.room.RoomDatabase
|
||||
-keep @androidx.room.Entity class *
|
||||
-dontwarn androidx.room.paging.**
|
||||
|
||||
# ============================================================
|
||||
# Data Classes & Models (Keep for serialization)
|
||||
# ============================================================
|
||||
-keep class io.novafoundation.nova.**.model.** { *; }
|
||||
-keep class io.novafoundation.nova.**.response.** { *; }
|
||||
-keep class io.novafoundation.nova.**.request.** { *; }
|
||||
-keep class io.novafoundation.nova.**.dto.** { *; }
|
||||
|
||||
# ============================================================
|
||||
# Parcelable
|
||||
# ============================================================
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Enums
|
||||
# ============================================================
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Serializable
|
||||
# ============================================================
|
||||
-keepclassmembers class * implements java.io.Serializable {
|
||||
static final long serialVersionUID;
|
||||
private static final java.io.ObjectStreamField[] serialPersistentFields;
|
||||
private void writeObject(java.io.ObjectOutputStream);
|
||||
private void readObject(java.io.ObjectInputStream);
|
||||
java.lang.Object writeReplace();
|
||||
java.lang.Object readResolve();
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Ledger USB/Bluetooth
|
||||
# ============================================================
|
||||
-keep class io.novafoundation.nova.feature_ledger_impl.** { *; }
|
||||
|
||||
# ============================================================
|
||||
# WalletConnect
|
||||
# ============================================================
|
||||
-keep class com.walletconnect.** { *; }
|
||||
-dontwarn com.walletconnect.**
|
||||
|
||||
# ============================================================
|
||||
# Optimization settings
|
||||
# ============================================================
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
-allowaccessmodification
|
||||
|
||||
# ============================================================
|
||||
# Don't warn about missing classes that we don't use
|
||||
# ============================================================
|
||||
-dontwarn org.conscrypt.**
|
||||
-dontwarn org.slf4j.**
|
||||
-dontwarn javax.naming.**
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.withChildScope
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeComponent
|
||||
import io.novafoundation.nova.runtime.ext.Geneses
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
open class BaseIntegrationTest {
|
||||
|
||||
protected val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
protected val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(context, RuntimeApi::class.java)
|
||||
|
||||
val chainRegistry = runtimeApi.chainRegistry()
|
||||
|
||||
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
|
||||
|
||||
@Before
|
||||
fun setup() = runBlocking {
|
||||
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
|
||||
}
|
||||
|
||||
protected fun runTest(action: suspend CoroutineScope.() -> Unit) {
|
||||
runBlocking {
|
||||
withChildScope {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected suspend fun ChainRegistry.polkadot(): Chain {
|
||||
return getChain(Chain.Geneses.POLKADOT)
|
||||
}
|
||||
|
||||
protected suspend fun ChainRegistry.polkadotAssetHub(): Chain {
|
||||
return getChain(Chain.Geneses.POLKADOT_ASSET_HUB)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindEventRecords
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.LOG_TAG
|
||||
import io.novafoundation.nova.common.utils.system
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeComponent
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storage
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storageKey
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BlockParsingIntegrationTest {
|
||||
|
||||
private val chainGenesis = "f1cf9022c7ebb34b162d5b5e34e705a5a740b2d0ecc1009fb89023e62a488108" // Shiden
|
||||
|
||||
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
RuntimeApi::class.java
|
||||
)
|
||||
|
||||
private val chainRegistry = runtimeApi.chainRegistry()
|
||||
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
|
||||
|
||||
private val rpcCalls = runtimeApi.rpcCalls()
|
||||
|
||||
private val remoteStorage = runtimeApi.remoteStorageSource()
|
||||
|
||||
@Test
|
||||
fun testBlockParsing() = runBlocking {
|
||||
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
|
||||
val chain = chainRegistry.getChain(chainGenesis)
|
||||
|
||||
val block = rpcCalls.getBlock(chain.id)
|
||||
|
||||
val logTag = this@BlockParsingIntegrationTest.LOG_TAG
|
||||
|
||||
Log.d(logTag, block.block.header.number.toString())
|
||||
|
||||
val events = remoteStorage.query(
|
||||
chainId = chain.id,
|
||||
keyBuilder = { it.metadata.system().storage("Events").storageKey() },
|
||||
binding = { scale, runtime ->
|
||||
Log.d(logTag, scale!!)
|
||||
bindEventRecords(scale)
|
||||
}
|
||||
)
|
||||
|
||||
// val eventsRaw = "0x0800000000000000000000000000000002000000010000000000585f8f0900000000020000"
|
||||
// val type = bindEventRecords(eventsRaw, chainRegistry.getRuntime(chain.id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.gson.Gson
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.common.data.network.NetworkApiCreator
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.core_db.model.chain.ChainLocal
|
||||
import io.novafoundation.nova.core_db.model.chain.NodeSelectionPreferencesLocal
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.RemoteToDomainChainMapperFacade
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainAssetToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainExplorerToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainExternalApiToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainLocalToChain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainNodeToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapChainToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapExternalApisToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapNodeSelectionPreferencesToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteAssetToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteChainToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteExplorersToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.mappers.mapRemoteNodesToLocal
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.ChainFetcher
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.model.ChainRemote
|
||||
import io.novafoundation.nova.test_shared.assertAllItemsEquals
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
CommonApi::class,
|
||||
RuntimeApi::class
|
||||
]
|
||||
)
|
||||
interface MappingTestAppComponent {
|
||||
|
||||
fun inject(test: ChainMappingIntegrationTest)
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ChainMappingIntegrationTest {
|
||||
|
||||
private val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
|
||||
private val featureContainer = context as FeatureContainer
|
||||
|
||||
@Inject
|
||||
lateinit var networkApiCreator: NetworkApiCreator
|
||||
|
||||
@Inject
|
||||
lateinit var remoteToDomainChainMapperFacade: RemoteToDomainChainMapperFacade
|
||||
|
||||
lateinit var chainFetcher: ChainFetcher
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
@Before
|
||||
fun prepare() {
|
||||
val component = DaggerMappingTestAppComponent.builder()
|
||||
.commonApi(featureContainer.commonApi())
|
||||
.runtimeApi(featureContainer.getFeature(RuntimeApi::class.java))
|
||||
.build()
|
||||
|
||||
component.inject(this)
|
||||
|
||||
chainFetcher = networkApiCreator.create(ChainFetcher::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testChainMappingIsMatch() {
|
||||
runBlocking {
|
||||
val chainsRemote = chainFetcher.getChains()
|
||||
|
||||
val remoteToDomain = chainsRemote.map { mapRemoteToDomain(it) }
|
||||
val remoteToLocalToDomain = chainsRemote.map { mapRemoteToLocalToDomain(it) }
|
||||
val domainToLocalToDomain = remoteToDomain.map { mapDomainToLocalToDomain(it) }
|
||||
|
||||
assertAllItemsEquals(listOf(remoteToDomain, remoteToLocalToDomain, domainToLocalToDomain))
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapRemoteToLocalToDomain(chainRemote: ChainRemote): Chain {
|
||||
val chainLocal = mapRemoteChainToLocal(chainRemote, null, ChainLocal.Source.DEFAULT, gson)
|
||||
val assetsLocal = chainRemote.assets.map { mapRemoteAssetToLocal(chainRemote, it, gson, isEnabled = true) }
|
||||
val nodesLocal = mapRemoteNodesToLocal(chainRemote)
|
||||
val explorersLocal = mapRemoteExplorersToLocal(chainRemote)
|
||||
val externalApisLocal = mapExternalApisToLocal(chainRemote)
|
||||
|
||||
return mapChainLocalToChain(
|
||||
chainLocal = chainLocal,
|
||||
nodesLocal = nodesLocal,
|
||||
nodeSelectionPreferences = NodeSelectionPreferencesLocal(chainLocal.id, autoBalanceEnabled = true, selectedNodeUrl = null),
|
||||
assetsLocal = assetsLocal,
|
||||
explorersLocal = explorersLocal,
|
||||
externalApisLocal = externalApisLocal,
|
||||
gson = gson
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapRemoteToDomain(chainRemote: ChainRemote): Chain {
|
||||
return remoteToDomainChainMapperFacade.mapRemoteChainToDomain(chainRemote, Chain.Source.DEFAULT)
|
||||
}
|
||||
|
||||
private fun mapDomainToLocalToDomain(chain: Chain): Chain {
|
||||
val chainLocal = mapChainToLocal(chain, gson)
|
||||
val nodesLocal = chain.nodes.nodes.map { mapChainNodeToLocal(it) }
|
||||
val nodeSelectionPreferencesLocal = mapNodeSelectionPreferencesToLocal(chain)
|
||||
val assetsLocal = chain.assets.map { mapChainAssetToLocal(it, gson) }
|
||||
val explorersLocal = chain.explorers.map { mapChainExplorerToLocal(it) }
|
||||
val externalApisLocal = chain.externalApis.map { mapChainExternalApiToLocal(gson, chain.id, it) }
|
||||
|
||||
return mapChainLocalToChain(
|
||||
chainLocal = chainLocal,
|
||||
nodesLocal = nodesLocal,
|
||||
nodeSelectionPreferences = nodeSelectionPreferencesLocal,
|
||||
assetsLocal = assetsLocal,
|
||||
explorersLocal = explorersLocal,
|
||||
externalApisLocal = externalApisLocal,
|
||||
gson = gson
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import androidx.room.Room
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.gson.Gson
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.common.data.network.NetworkApiCreator
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.core_db.AppDatabase
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.ChainSyncService
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.remote.ChainFetcher
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
CommonApi::class,
|
||||
]
|
||||
)
|
||||
interface TestAppComponent {
|
||||
|
||||
fun inject(test: ChainSyncServiceIntegrationTest)
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ChainSyncServiceIntegrationTest {
|
||||
|
||||
private val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
|
||||
private val featureContainer = context as FeatureContainer
|
||||
|
||||
@Inject
|
||||
lateinit var networkApiCreator: NetworkApiCreator
|
||||
|
||||
lateinit var chainSyncService: ChainSyncService
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val component = DaggerTestAppComponent.builder()
|
||||
.commonApi(featureContainer.commonApi())
|
||||
.build()
|
||||
|
||||
component.inject(this)
|
||||
|
||||
val chainDao = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
|
||||
.build()
|
||||
.chainDao()
|
||||
|
||||
chainSyncService = ChainSyncService(chainDao, networkApiCreator.create(ChainFetcher::class.java), Gson())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchAndStoreRealChains() = runBlocking {
|
||||
chainSyncService.syncUp()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.address.intoKey
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.emptySubstrateAccountId
|
||||
import io.novafoundation.nova.feature_account_api.data.fee.FeePaymentCurrency
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.toDefaultSubstrateAddress
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.assets.tranfers.AssetTransferBase
|
||||
import io.novafoundation.nova.feature_wallet_api.data.repository.getXcmChain
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.legacy.transferConfiguration
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.model.xcm.transferConfiguration
|
||||
import io.novafoundation.nova.runtime.ext.addressOf
|
||||
import io.novafoundation.nova.runtime.ext.emptyAccountId
|
||||
import io.novafoundation.nova.runtime.ext.normalizeTokenSymbol
|
||||
import io.novafoundation.nova.runtime.multiNetwork.findChain
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
class CrossChainTransfersIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
WalletFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val chainTransfersRepository = walletApi.crossChainTransfersRepository
|
||||
private val crossChainWeigher = walletApi.crossChainWeigher
|
||||
|
||||
private val parachainInfoRepository = runtimeApi.parachainInfoRepository
|
||||
|
||||
@Test
|
||||
fun testParachainToParachain() = performFeeTest(
|
||||
from = "Moonriver",
|
||||
what = "xcKAR",
|
||||
to = "Karura"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testRelaychainToParachain() = performFeeTest(
|
||||
from = "Kusama",
|
||||
what = "KSM",
|
||||
to = "Moonriver"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testParachainToRelaychain() = performFeeTest(
|
||||
from = "Moonriver",
|
||||
what = "xcKSM",
|
||||
to = "Kusama"
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testParachainToParachainNonReserve() = performFeeTest(
|
||||
from = "Karura",
|
||||
what = "BNC",
|
||||
to = "Moonriver"
|
||||
)
|
||||
|
||||
private fun performFeeTest(
|
||||
from: String,
|
||||
to: String,
|
||||
what: String
|
||||
) {
|
||||
runBlocking {
|
||||
val originChain = chainRegistry.findChain { it.name == from }!!
|
||||
val asssetInOrigin = originChain.assets.first { it.symbol.value == what }
|
||||
|
||||
val destinationChain = chainRegistry.findChain { it.name == to }!!
|
||||
val asssetInDestination = destinationChain.assets.first { normalizeTokenSymbol(it.symbol.value) == normalizeTokenSymbol(what) }
|
||||
|
||||
val crossChainConfig = chainTransfersRepository.getConfiguration()
|
||||
|
||||
val crossChainTransfer = crossChainConfig.transferConfiguration(
|
||||
originChain = parachainInfoRepository.getXcmChain(originChain),
|
||||
originAsset = asssetInOrigin,
|
||||
destinationChain = parachainInfoRepository.getXcmChain(destinationChain),
|
||||
)!!
|
||||
|
||||
val transfer = AssetTransferBase(
|
||||
recipient = originChain.addressOf(originChain.emptyAccountId()),
|
||||
originChain = originChain,
|
||||
originChainAsset = asssetInOrigin,
|
||||
destinationChain = destinationChain,
|
||||
destinationChainAsset = asssetInDestination,
|
||||
feePaymentCurrency = FeePaymentCurrency.Native,
|
||||
amountPlanks = BigInteger.ZERO
|
||||
)
|
||||
|
||||
val crossChainFeeResult = runCatching { crossChainWeigher.estimateFee(transfer, crossChainTransfer) }
|
||||
|
||||
check(crossChainFeeResult.isSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.address.intoKey
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.toResult
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.composeCall
|
||||
import io.novafoundation.nova.common.utils.xcmPalletName
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.model.planksFromAmount
|
||||
import io.novafoundation.nova.feature_xcm_api.asset.MultiAsset
|
||||
import io.novafoundation.nova.feature_xcm_api.asset.MultiAssets
|
||||
import io.novafoundation.nova.feature_xcm_api.di.XcmFeatureApi
|
||||
import io.novafoundation.nova.feature_xcm_api.runtimeApi.dryRun.model.OriginCaller
|
||||
import io.novafoundation.nova.feature_xcm_api.runtimeApi.dryRun.model.getByLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.Junctions
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.MultiLocation.Junction.ParachainId
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.asLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.multiLocation.toMultiLocation
|
||||
import io.novafoundation.nova.feature_xcm_api.versions.XcmVersion
|
||||
import io.novafoundation.nova.feature_xcm_api.versions.toEncodableInstance
|
||||
import io.novafoundation.nova.feature_xcm_api.versions.versionedXcm
|
||||
import io.novafoundation.nova.feature_xcm_api.weight.WeightLimit
|
||||
import io.novafoundation.nova.runtime.ext.utilityAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
class DryRunIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val xcmApi = FeatureUtils.getFeature<XcmFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
XcmFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val dryRunApi = xcmApi.dryRunApi
|
||||
|
||||
@Test
|
||||
fun testDryRunXcmTeleport() = runTest {
|
||||
val polkadot = chainRegistry.polkadot()
|
||||
val polkadotAssetHub = chainRegistry.polkadotAssetHub()
|
||||
|
||||
val polkadotRuntime = chainRegistry.getRuntime(polkadot.id)
|
||||
|
||||
val polkadotLocation = MultiLocation.Interior.Here.asLocation()
|
||||
val polkadotAssetHubLocation = Junctions(ParachainId(1000)).asLocation()
|
||||
|
||||
val dotLocation = polkadotLocation.toRelative()
|
||||
val amount = polkadot.utilityAsset.planksFromAmount(BigDecimal.ONE)
|
||||
val assets = MultiAsset.from(dotLocation, amount)
|
||||
|
||||
val origin = "16WWmr2Xqgy5fna35GsNHXMU7vDBM12gzHCFGibQjSmKpAN".toAccountId().intoKey()
|
||||
val beneficiary = origin.toMultiLocation()
|
||||
|
||||
val xcmVersion = XcmVersion.V4
|
||||
|
||||
val pahVersionedLocation = polkadotAssetHubLocation.toRelative().versionedXcm(xcmVersion)
|
||||
|
||||
// Compose limited_teleport_assets call to execute on Polkadot
|
||||
val call = polkadotRuntime.composeCall(
|
||||
moduleName = polkadotRuntime.metadata.xcmPalletName(),
|
||||
callName = "limited_teleport_assets",
|
||||
arguments = mapOf(
|
||||
"dest" to pahVersionedLocation.toEncodableInstance(),
|
||||
"beneficiary" to beneficiary.versionedXcm(xcmVersion).toEncodableInstance(),
|
||||
"assets" to MultiAssets(assets).versionedXcm(xcmVersion).toEncodableInstance(),
|
||||
"fee_asset_item" to BigInteger.ZERO,
|
||||
"weight_limit" to WeightLimit.Unlimited.toEncodableInstance()
|
||||
)
|
||||
)
|
||||
|
||||
// Dry run call execution
|
||||
val dryRunEffects = dryRunApi.dryRunCall(
|
||||
originCaller = OriginCaller.System.Signed(origin),
|
||||
call = call,
|
||||
chainId = polkadot.id,
|
||||
xcmResultsVersion = XcmVersion.V4
|
||||
)
|
||||
.getOrThrow()
|
||||
.toResult().getOrThrow()
|
||||
|
||||
// Find xcm forwarded to Polkadot Asset Hub
|
||||
val forwardedXcm = dryRunEffects.forwardedXcms.getByLocation(pahVersionedLocation).first()
|
||||
println(forwardedXcm)
|
||||
|
||||
// Dry run execution of forwarded message on Polkadot Asset Hub
|
||||
val xcmDryRunEffects = dryRunApi.dryRunXcm(
|
||||
xcm = forwardedXcm,
|
||||
originLocation = polkadotLocation.fromPointOfViewOf(polkadotAssetHubLocation).versionedXcm(xcmVersion),
|
||||
chainId = polkadotAssetHub.id
|
||||
)
|
||||
.getOrThrow()
|
||||
.toResult().getOrThrow()
|
||||
|
||||
println(xcmDryRunEffects.emittedEvents)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.utils.average
|
||||
import io.novafoundation.nova.common.utils.divideToDecimal
|
||||
import io.novafoundation.nova.runtime.ethereum.gas.LegacyGasPriceProvider
|
||||
import io.novafoundation.nova.runtime.ethereum.gas.MaxPriorityFeeGasProvider
|
||||
import io.novafoundation.nova.runtime.ext.Ids
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getCallEthereumApiOrThrow
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.take
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
class GasPriceProviderIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
@Test
|
||||
fun compareLegacyAndImprovedGasPriceEstimations() = runTest {
|
||||
val api = chainRegistry.getCallEthereumApiOrThrow(Chain.Ids.MOONBEAM)
|
||||
|
||||
val legacy = LegacyGasPriceProvider(api)
|
||||
val improved = MaxPriorityFeeGasProvider(api)
|
||||
|
||||
val legacyStats = mutableSetOf<BigInteger>()
|
||||
val improvedStats = mutableSetOf<BigInteger>()
|
||||
|
||||
api.newHeadsFlow().map {
|
||||
legacyStats.add(legacy.getGasPrice())
|
||||
improvedStats.add(improved.getGasPrice())
|
||||
}
|
||||
.take(1000)
|
||||
.collect()
|
||||
|
||||
legacyStats.printStats("Legacy")
|
||||
improvedStats.printStats("Improved")
|
||||
}
|
||||
|
||||
private fun Set<BigInteger>.printStats(name: String) {
|
||||
val min = min()
|
||||
val max = max()
|
||||
|
||||
Log.d("GasPriceProviderIntegrationTest", """
|
||||
Stats for $name source
|
||||
Min: $min
|
||||
Max: $max
|
||||
Avg: ${average()}
|
||||
Max/Min ratio: ${max.divideToDecimal(min)}
|
||||
""")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.LOG_TAG
|
||||
import io.novafoundation.nova.common.utils.firstLoaded
|
||||
import io.novafoundation.nova.common.utils.inBackground
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.ReferendumId
|
||||
import io.novafoundation.nova.feature_governance_api.data.network.blockhain.model.VoteType
|
||||
import io.novafoundation.nova.feature_governance_api.data.source.SupportedGovernanceOption
|
||||
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_api.domain.referendum.filters.ReferendumType
|
||||
import io.novafoundation.nova.feature_governance_api.domain.referendum.filters.ReferendumTypeFilter
|
||||
import io.novafoundation.nova.feature_governance_impl.data.RealGovernanceAdditionalState
|
||||
import io.novafoundation.nova.runtime.ext.externalApi
|
||||
import io.novafoundation.nova.runtime.ext.utilityAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainWithAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.FullChainAssetId
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
|
||||
class GovernanceIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val accountApi = FeatureUtils.getFeature<AccountFeatureApi>(context, AccountFeatureApi::class.java)
|
||||
private val governanceApi = FeatureUtils.getFeature<GovernanceFeatureApi>(context, GovernanceFeatureApi::class.java)
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveOnChainReferenda() = runTest {
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val onChainReferendaRepository = source(selectedGovernance).referenda
|
||||
|
||||
val referenda = onChainReferendaRepository.getAllOnChainReferenda(chain.id)
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referenda.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveConvictionVotes() = runTest {
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val convictionVotingRepository = source(selectedGovernance).convictionVoting
|
||||
|
||||
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
|
||||
|
||||
val votes = convictionVotingRepository.votingFor(accountId, chain.id)
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, votes.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveTrackLocks() = runTest {
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val convictionVotingRepository = source(selectedGovernance).convictionVoting
|
||||
|
||||
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
|
||||
|
||||
val fullChainAssetId = FullChainAssetId(chain.id, chain.utilityAsset.id)
|
||||
|
||||
val trackLocks = convictionVotingRepository.trackLocksFlow(accountId, fullChainAssetId).first()
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, trackLocks.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveReferendaTracks() = runTest {
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val onChainReferendaRepository = source(selectedGovernance).referenda
|
||||
|
||||
val tracks = onChainReferendaRepository.getTracks(chain.id)
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, tracks.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveDomainReferendaPreviews() = runTest {
|
||||
val accountRepository = accountApi.provideAccountRepository()
|
||||
val referendaListInteractor = governanceApi.referendaListInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val metaAccount = accountRepository.getSelectedMetaAccount()
|
||||
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
|
||||
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val filterFlow: Flow<ReferendumTypeFilter> = flow {
|
||||
val referendaFilter = ReferendumTypeFilter(ReferendumType.ALL)
|
||||
emit(referendaFilter)
|
||||
}
|
||||
|
||||
val referendaByGroup = referendaListInteractor.referendaListStateFlow(metaAccount, accountId, selectedGovernance, this, filterFlow).firstLoaded()
|
||||
val referenda = referendaByGroup.groupedReferenda.values.flatten()
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referenda.joinToString("\n"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveDomainReferendumDetails() = runTest {
|
||||
val referendumDetailsInteractor = governanceApi.referendumDetailsInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val accountId = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".toAccountId()
|
||||
val referendumId = ReferendumId(BigInteger.ZERO)
|
||||
val chain = chain()
|
||||
val selectedGovernance = supportedGovernanceOption(chain, Chain.Governance.V1)
|
||||
|
||||
val referendumDetails = referendumDetailsInteractor.referendumDetailsFlow(referendumId, selectedGovernance, accountId, CoroutineScope(Dispatchers.Main))
|
||||
.first()
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referendumDetails.toString())
|
||||
|
||||
val callDetails = referendumDetailsInteractor.detailsFor(
|
||||
preImage = referendumDetails?.onChainMetadata!!.preImage!!,
|
||||
chain = chain
|
||||
)
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, callDetails.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldRetrieveVoters() = runTest {
|
||||
val interactor = governanceApi.referendumVotersInteractor
|
||||
|
||||
val referendumId = ReferendumId(BigInteger.ZERO)
|
||||
val referendumVoters = interactor.votersFlow(referendumId, VoteType.AYE)
|
||||
.first()
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, referendumVoters.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchDelegatesList() = runTest {
|
||||
val interactor = governanceApi.delegateListInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val chain = kusama()
|
||||
val delegates = interactor.getDelegates(
|
||||
governanceOption = supportedGovernanceOption(chain, Chain.Governance.V2),
|
||||
scope = this
|
||||
)
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegates.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchDelegateDetails() = runTest {
|
||||
val delegateAccountId = "DCZyhphXsRLcW84G9WmWEXtAA8DKGtVGSFZLJYty8Ajjyfa".toAccountId() // ChaosDAO
|
||||
|
||||
val interactor = governanceApi.delegateDetailsInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val delegate = interactor.delegateDetailsFlow(delegateAccountId)
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegate.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchChooseTrackData() = runTest {
|
||||
val interactor = governanceApi.newDelegationChooseTrackInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val trackData = interactor.observeNewDelegationTrackData().first()
|
||||
|
||||
Log.d(
|
||||
this@GovernanceIntegrationTest.LOG_TAG,
|
||||
"""
|
||||
Available: ${trackData.trackPartition.available.size}
|
||||
Already voted: ${trackData.trackPartition.alreadyVoted.size}
|
||||
Already delegated: ${trackData.trackPartition.alreadyDelegated.size}
|
||||
Presets: ${trackData.presets}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchDelegators() = runTest {
|
||||
val delegateAddress = "DCZyhphXsRLcW84G9WmWEXtAA8DKGtVGSFZLJYty8Ajjyfa" // ChaosDAO
|
||||
|
||||
val interactor = governanceApi.delegateDelegatorsInteractor
|
||||
val updateSystem = governanceApi.governanceUpdateSystem
|
||||
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
val delegators = interactor.delegatorsFlow(delegateAddress.toAccountId()).first()
|
||||
|
||||
Log.d(this@GovernanceIntegrationTest.LOG_TAG, delegators.toString())
|
||||
}
|
||||
|
||||
private suspend fun source(supportedGovernance: SupportedGovernanceOption) = governanceApi.governanceSourceRegistry.sourceFor(supportedGovernance)
|
||||
|
||||
private fun supportedGovernanceOption(chain: Chain, governance: Chain.Governance) =
|
||||
SupportedGovernanceOption(
|
||||
ChainWithAsset(chain, chain.utilityAsset),
|
||||
RealGovernanceAdditionalState(
|
||||
governance,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
private suspend fun chain(): Chain = chainRegistry.currentChains.map { chains ->
|
||||
chains.find { it.governance.isNotEmpty() }
|
||||
}
|
||||
.filterNotNull()
|
||||
.first()
|
||||
|
||||
private suspend fun kusama(): Chain = chainRegistry.currentChains.mapNotNull { chains ->
|
||||
chains.find { it.externalApi<Chain.ExternalApi.GovernanceDelegations>() != null }
|
||||
}.first()
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,60 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
||||
import io.novafoundation.nova.feature_nft_api.data.model.isFullySynced
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeComponent
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
|
||||
class NftFullSyncIntegrationTest {
|
||||
|
||||
private val nftApi = FeatureUtils.getFeature<NftFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
NftFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val accountApi = FeatureUtils.getFeature<AccountFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
AccountFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
RuntimeApi::class.java
|
||||
)
|
||||
|
||||
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
|
||||
|
||||
@Test
|
||||
fun testFullSyncIntegration(): Unit = runBlocking {
|
||||
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
|
||||
|
||||
val metaAccount = accountApi.accountUseCase().getSelectedMetaAccount()
|
||||
|
||||
val nftRepository = nftApi.nftRepository
|
||||
|
||||
nftRepository.initialNftSync(metaAccount, true)
|
||||
|
||||
nftRepository.allNftFlow(metaAccount)
|
||||
.map { nfts -> nfts.filter { !it.isFullySynced } }
|
||||
.takeWhile { it.isNotEmpty() }
|
||||
.onEach { unsyncedNfts ->
|
||||
unsyncedNfts.forEach { nftRepository.fullNftSync(it) }
|
||||
}
|
||||
.onCompletion {
|
||||
print("Full sync done")
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountId
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindBoolean
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindString
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.cast
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.getTyped
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.LOG_TAG
|
||||
import io.novafoundation.nova.common.utils.uniques
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeComponent
|
||||
import io.novafoundation.nova.runtime.ext.addressOf
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import io.novafoundation.nova.runtime.storage.source.multi.MultiQueryBuilder
|
||||
import io.novafoundation.nova.runtime.storage.source.query.multi
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.Struct
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storage
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
data class UniquesClass(
|
||||
val id: BigInteger,
|
||||
val metadata: Metadata?,
|
||||
val details: Details
|
||||
) {
|
||||
data class Metadata(
|
||||
val deposit: BigInteger,
|
||||
val data: String,
|
||||
)
|
||||
|
||||
data class Details(
|
||||
val instances: BigInteger,
|
||||
val frozen: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
data class UniquesInstance(
|
||||
val collection: UniquesClass,
|
||||
val id: BigInteger,
|
||||
val metadata: Metadata?,
|
||||
val details: Details
|
||||
) {
|
||||
|
||||
data class Metadata(
|
||||
val data: String,
|
||||
)
|
||||
|
||||
data class Details(
|
||||
val owner: String,
|
||||
val frozen: Boolean,
|
||||
)
|
||||
}
|
||||
|
||||
class NftUniquesIntegrationTest {
|
||||
|
||||
private val chainGenesis = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a"
|
||||
|
||||
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
RuntimeApi::class.java
|
||||
)
|
||||
|
||||
private val chainRegistry = runtimeApi.chainRegistry()
|
||||
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
|
||||
|
||||
private val storageRemoteSource = runtimeApi.remoteStorageSource()
|
||||
|
||||
@Test
|
||||
fun testUniquesIntegration(): Unit = runBlocking {
|
||||
chainRegistry.currentChains.first() // wait till chains are ready
|
||||
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
|
||||
|
||||
val chain = chainRegistry.getChain(chainGenesis)
|
||||
|
||||
val accountId = "JGKSibhyZgzY7jEe5a9gdybDEbqNNRSxYyJJmeeycbCbQ5v".toAccountId()
|
||||
|
||||
val instances = storageRemoteSource.query(chainGenesis) {
|
||||
val classesWithInstances = runtime.metadata.uniques().storage("Account").keys(accountId)
|
||||
.map { (_: AccountId, collection: BigInteger, instance: BigInteger) ->
|
||||
listOf(collection, instance)
|
||||
}
|
||||
|
||||
val classesIds = classesWithInstances.map { (collection, _) -> collection }.distinct()
|
||||
|
||||
val classDetailsDescriptor: MultiQueryBuilder.Descriptor<BigInteger, UniquesClass.Details>
|
||||
val classMetadatasDescriptor: MultiQueryBuilder.Descriptor<BigInteger, UniquesClass.Metadata?>
|
||||
val instancesDetailsDescriptor: MultiQueryBuilder.Descriptor<Pair<BigInteger, BigInteger>, UniquesInstance.Details>
|
||||
val instancesMetadataDescriptor: MultiQueryBuilder.Descriptor<Pair<BigInteger, BigInteger>, UniquesInstance.Metadata?>
|
||||
|
||||
val multiQueryResults = multi {
|
||||
classDetailsDescriptor = runtime.metadata.uniques().storage("Class").querySingleArgKeys(
|
||||
keysArgs = classesIds,
|
||||
keyExtractor = { it.component1<BigInteger>() },
|
||||
binding = { parsedValue ->
|
||||
val classDetailsStruct = parsedValue.cast<Struct.Instance>()
|
||||
|
||||
UniquesClass.Details(
|
||||
instances = classDetailsStruct.getTyped("instances"),
|
||||
frozen = classDetailsStruct.getTyped("isFrozen")
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
classMetadatasDescriptor = runtime.metadata.uniques().storage("ClassMetadataOf").querySingleArgKeys(
|
||||
keysArgs = classesIds,
|
||||
keyExtractor = { it.component1<BigInteger>() },
|
||||
binding = { parsedValue ->
|
||||
parsedValue?.cast<Struct.Instance>()?.let { classMetadataStruct ->
|
||||
UniquesClass.Metadata(
|
||||
deposit = classMetadataStruct.getTyped("deposit"),
|
||||
data = bindString(classMetadataStruct["data"])
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
instancesDetailsDescriptor = runtime.metadata.uniques().storage("Asset").queryKeys(
|
||||
keysArgs = classesWithInstances,
|
||||
keyExtractor = { it.component1<BigInteger>() to it.component2<BigInteger>() },
|
||||
binding = { parsedValue ->
|
||||
val instanceDetailsStruct = parsedValue.cast<Struct.Instance>()
|
||||
|
||||
UniquesInstance.Details(
|
||||
owner = chain.addressOf(bindAccountId(instanceDetailsStruct["owner"])),
|
||||
frozen = bindBoolean(instanceDetailsStruct["isFrozen"])
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
instancesMetadataDescriptor = runtime.metadata.uniques().storage("InstanceMetadataOf").queryKeys(
|
||||
keysArgs = classesWithInstances,
|
||||
keyExtractor = { it.component1<BigInteger>() to it.component2<BigInteger>() },
|
||||
binding = { parsedValue ->
|
||||
parsedValue?.cast<Struct.Instance>()?.let {
|
||||
UniquesInstance.Metadata(
|
||||
data = bindString(it["data"])
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val classDetails = multiQueryResults[classDetailsDescriptor]
|
||||
|
||||
val classMetadatas = multiQueryResults[classMetadatasDescriptor]
|
||||
|
||||
val instancesDetails = multiQueryResults[instancesDetailsDescriptor]
|
||||
|
||||
val instancesMetadatas = multiQueryResults[instancesMetadataDescriptor]
|
||||
|
||||
val classes = classesIds.associateWith { classId ->
|
||||
UniquesClass(
|
||||
id = classId,
|
||||
metadata = classMetadatas[classId],
|
||||
details = classDetails.getValue(classId)
|
||||
)
|
||||
}
|
||||
|
||||
classesWithInstances.map { (collectionId, instanceId) ->
|
||||
val instanceKey = collectionId to instanceId
|
||||
|
||||
UniquesInstance(
|
||||
collection = classes.getValue(collectionId),
|
||||
id = instanceId,
|
||||
metadata = instancesMetadatas[instanceKey],
|
||||
details = instancesDetails.getValue(instanceKey)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, instances.toString())
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.Fraction
|
||||
import io.novafoundation.nova.common.utils.Perbill
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_api.domain.nominationPool.model.PoolId
|
||||
import io.novafoundation.nova.feature_staking_impl.data.StakingOption
|
||||
import io.novafoundation.nova.feature_staking_impl.data.StakingSharedState.OptionAdditionalData
|
||||
import io.novafoundation.nova.feature_staking_impl.di.StakingFeatureComponent
|
||||
import io.novafoundation.nova.feature_staking_impl.domain.nominationPools.common.rewards.NominationPoolRewardCalculator
|
||||
import io.novafoundation.nova.runtime.ext.utilityAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainWithAsset
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain.Asset.StakingType
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import org.junit.Test
|
||||
|
||||
class NominationPoolsRewardCalculatorIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val stakingFeatureComponent = FeatureUtils.getFeature<StakingFeatureComponent>(context, StakingFeatureApi::class.java)
|
||||
|
||||
private val nominationPoolRewardCalculatorFactory = stakingFeatureComponent.nominationPoolRewardCalculatorFactory
|
||||
private val stakingUpdateSystem = stakingFeatureComponent.stakingUpdateSystem
|
||||
private val stakingSharedState = stakingFeatureComponent.stakingSharedState
|
||||
|
||||
@Test
|
||||
fun testRewardCalculator() = runTest {
|
||||
val polkadot = chainRegistry.polkadot()
|
||||
val stakingOption = StakingOption(
|
||||
assetWithChain = ChainWithAsset(polkadot, polkadot.utilityAsset),
|
||||
additional = OptionAdditionalData(StakingType.NOMINATION_POOLS)
|
||||
)
|
||||
|
||||
stakingSharedState.setSelectedOption(stakingOption)
|
||||
|
||||
stakingUpdateSystem.start()
|
||||
.launchIn(this)
|
||||
|
||||
val rewardCalculator = nominationPoolRewardCalculatorFactory.create(stakingOption, sharedComputationScope = this)
|
||||
|
||||
Log.d("NominationPoolsRewardCalculatorIntegrationTest", "Max APY: ${rewardCalculator.maxAPY}")
|
||||
Log.d("NominationPoolsRewardCalculatorIntegrationTest", "APY for Nova Pool: ${rewardCalculator.apyFor(54)}")
|
||||
}
|
||||
|
||||
private fun NominationPoolRewardCalculator.apyFor(poolId: Int): Fraction? {
|
||||
return apyFor(PoolId(poolId))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.balances
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.CallExecutionType
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.NovaSigner
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningContext
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningMode
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SubmissionHierarchy
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.setSignerData
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.nativeTransfer
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.runtime.ext.Geneses
|
||||
import io.novafoundation.nova.runtime.ext.utilityAsset
|
||||
import io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novasama.substrate_sdk_android.encrypt.SignatureWrapper
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.BatchMode
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.Nonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.InheritedImplication
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.CheckNonce.Companion.setNonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.GeneralTransactionSigner
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.VerifySignature.Companion.setVerifySignature
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.callOrNull
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.moduleOrNull
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* End-to-end integration tests for Pezkuwi chain compatibility.
|
||||
* These tests verify that:
|
||||
* 1. Runtime loads correctly with proper types
|
||||
* 2. Extrinsics can be built
|
||||
* 3. Fee calculation works
|
||||
* 4. Transfer extrinsics can be created
|
||||
*
|
||||
* Run with: ./gradlew :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.novafoundation.nova.PezkuwiIntegrationTest
|
||||
*/
|
||||
class PezkuwiIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
WalletFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val extrinsicBuilderFactory = runtimeApi.provideExtrinsicBuilderFactory()
|
||||
private val rpcCalls = runtimeApi.rpcCalls()
|
||||
|
||||
/**
|
||||
* Test 1: Verify Pezkuwi Mainnet runtime loads with required types
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiMainnetRuntimeTypes() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
// Verify critical types exist
|
||||
val extrinsicSignature = runtime.typeRegistry["ExtrinsicSignature"]
|
||||
assertNotNull("ExtrinsicSignature type should exist", extrinsicSignature)
|
||||
|
||||
val multiSignature = runtime.typeRegistry["MultiSignature"]
|
||||
assertNotNull("MultiSignature type should exist", multiSignature)
|
||||
|
||||
val multiAddress = runtime.typeRegistry["MultiAddress"]
|
||||
assertNotNull("MultiAddress type should exist", multiAddress)
|
||||
|
||||
val address = runtime.typeRegistry["Address"]
|
||||
assertNotNull("Address type should exist", address)
|
||||
|
||||
println("Pezkuwi Mainnet: All required types present")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: Verify Pezkuwi Asset Hub runtime loads with required types
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiAssetHubRuntimeTypes() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI_ASSET_HUB)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
val extrinsicSignature = runtime.typeRegistry["ExtrinsicSignature"]
|
||||
assertNotNull("ExtrinsicSignature type should exist", extrinsicSignature)
|
||||
|
||||
val address = runtime.typeRegistry["Address"]
|
||||
assertNotNull("Address type should exist", address)
|
||||
|
||||
println("Pezkuwi Asset Hub: All required types present")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: Verify extrinsic builder can be created for Pezkuwi
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiExtrinsicBuilderCreation() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory.Options(
|
||||
batchMode = BatchMode.BATCH_ALL
|
||||
)
|
||||
)
|
||||
|
||||
assertNotNull("ExtrinsicBuilder should be created", builder)
|
||||
println("Pezkuwi ExtrinsicBuilder created successfully")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Verify transfer call can be constructed
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiTransferCallConstruction() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
// Check if balances module exists
|
||||
val balancesModule = runtime.metadata.moduleOrNull("Balances")
|
||||
assertNotNull("Balances module should exist", balancesModule)
|
||||
|
||||
// Check transfer call exists
|
||||
val hasTransferKeepAlive = balancesModule?.callOrNull("transfer_keep_alive") != null
|
||||
val hasTransferAllowDeath = balancesModule?.callOrNull("transfer_allow_death") != null ||
|
||||
balancesModule?.callOrNull("transfer") != null
|
||||
|
||||
assertTrue("Transfer call should exist", hasTransferKeepAlive || hasTransferAllowDeath)
|
||||
|
||||
println("Pezkuwi transfer call found: transfer_keep_alive=$hasTransferKeepAlive, transfer_allow_death=$hasTransferAllowDeath")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 5: Verify signed extensions are properly handled
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiSignedExtensions() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
val signedExtensions = runtime.metadata.extrinsic.signedExtensions.map { it.id }
|
||||
println("Pezkuwi signed extensions: $signedExtensions")
|
||||
|
||||
// Verify Pezkuwi-specific extensions
|
||||
val hasAuthorizeCall = signedExtensions.contains("AuthorizeCall")
|
||||
println("Has AuthorizeCall extension: $hasAuthorizeCall")
|
||||
|
||||
// Standard extensions should also be present
|
||||
val hasCheckMortality = signedExtensions.contains("CheckMortality")
|
||||
val hasCheckNonce = signedExtensions.contains("CheckNonce")
|
||||
|
||||
assertTrue("CheckMortality should exist", hasCheckMortality)
|
||||
assertTrue("CheckNonce should exist", hasCheckNonce)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 6: Verify utility asset is properly configured
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiUtilityAsset() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
|
||||
val utilityAsset = chain.utilityAsset
|
||||
assertNotNull("Utility asset should exist", utilityAsset)
|
||||
|
||||
println("Pezkuwi utility asset: ${utilityAsset.symbol}, precision: ${utilityAsset.precision}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 7: Build and sign a transfer extrinsic (THIS IS THE CRITICAL TEST)
|
||||
* This test will catch "TypeReference is null" errors during signing
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiBuildSignedTransferExtrinsic() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val signer = TestSigner()
|
||||
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
|
||||
// Add transfer call
|
||||
val recipientAccountId = ByteArray(32) { 2 }
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = BigInteger.ONE)
|
||||
|
||||
// Set signer data (this is where TypeReference errors can occur)
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(TestSigningContext(chain), SigningMode.SUBMISSION)
|
||||
}
|
||||
Log.d("PezkuwiTest", "Signer data set successfully")
|
||||
} catch (e: Exception) {
|
||||
Log.e("PezkuwiTest", "Failed to set signer data", e)
|
||||
fail("Failed to set signer data: ${e.message}")
|
||||
}
|
||||
|
||||
// Build the extrinsic (this is where TypeReference errors can also occur)
|
||||
try {
|
||||
val extrinsic = builder.buildExtrinsic()
|
||||
assertNotNull("Built extrinsic should not be null", extrinsic)
|
||||
Log.d("PezkuwiTest", "Extrinsic built successfully: ${extrinsic.extrinsicHex}")
|
||||
println("Pezkuwi: Transfer extrinsic built and signed successfully!")
|
||||
} catch (e: Exception) {
|
||||
Log.e("PezkuwiTest", "Failed to build extrinsic", e)
|
||||
fail("Failed to build extrinsic: ${e.message}\nCause: ${e.cause?.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 8: Build extrinsic for fee calculation (uses fake signature)
|
||||
*/
|
||||
@Test
|
||||
fun testPezkuwiBuildFeeExtrinsic() = runTest {
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val signer = TestSigner()
|
||||
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
|
||||
val recipientAccountId = ByteArray(32) { 2 }
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = BigInteger.ONE)
|
||||
|
||||
// Set signer data for FEE mode (uses fake signature)
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(TestSigningContext(chain), SigningMode.FEE)
|
||||
}
|
||||
val extrinsic = builder.buildExtrinsic()
|
||||
assertNotNull("Fee extrinsic should not be null", extrinsic)
|
||||
println("Pezkuwi: Fee extrinsic built successfully!")
|
||||
} catch (e: Exception) {
|
||||
Log.e("PezkuwiTest", "Failed to build fee extrinsic", e)
|
||||
fail("Failed to build fee extrinsic: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper extension
|
||||
private suspend fun ChainRegistry.pezkuwiMainnet(): Chain {
|
||||
return getChain(Chain.Geneses.PEZKUWI)
|
||||
}
|
||||
|
||||
// Test signer for building extrinsics without real keys
|
||||
private inner class TestSigner : NovaSigner, GeneralTransactionSigner {
|
||||
|
||||
val accountId = ByteArray(32) { 1 }
|
||||
|
||||
override suspend fun callExecutionType(): CallExecutionType {
|
||||
return CallExecutionType.IMMEDIATE
|
||||
}
|
||||
|
||||
override val metaAccount: MetaAccount = DefaultMetaAccount(
|
||||
id = 0,
|
||||
globallyUniqueId = "0",
|
||||
substrateAccountId = accountId,
|
||||
substrateCryptoType = null,
|
||||
substratePublicKey = null,
|
||||
ethereumAddress = null,
|
||||
ethereumPublicKey = null,
|
||||
isSelected = true,
|
||||
name = "test",
|
||||
type = LightMetaAccount.Type.SECRETS,
|
||||
chainAccounts = emptyMap(),
|
||||
status = LightMetaAccount.Status.ACTIVE,
|
||||
parentMetaId = null
|
||||
)
|
||||
|
||||
override suspend fun getSigningHierarchy(): SubmissionHierarchy {
|
||||
return SubmissionHierarchy(metaAccount, CallExecutionType.IMMEDIATE)
|
||||
}
|
||||
|
||||
override suspend fun signRaw(payload: SignerPayloadRaw): SignedRaw {
|
||||
error("Not implemented")
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForSubmission(context: SigningContext) {
|
||||
setNonce(BigInteger.ZERO)
|
||||
setVerifySignature(this@TestSigner, accountId)
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForFee(context: SigningContext) {
|
||||
setSignerDataForSubmission(context)
|
||||
}
|
||||
|
||||
override suspend fun submissionSignerAccountId(chain: Chain): AccountId {
|
||||
return accountId
|
||||
}
|
||||
|
||||
override suspend fun maxCallsPerTransaction(): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun signInheritedImplication(
|
||||
inheritedImplication: InheritedImplication,
|
||||
accountId: AccountId
|
||||
): SignatureWrapper {
|
||||
// Return a fake Sr25519 signature for testing
|
||||
return SignatureWrapper.Sr25519(ByteArray(64))
|
||||
}
|
||||
}
|
||||
|
||||
private class TestSigningContext(override val chain: Chain) : SigningContext {
|
||||
override suspend fun getNonce(accountId: AccountIdKey): Nonce {
|
||||
return Nonce.ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.novafoundation.nova.common.address.AccountIdKey
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.deriveSeed32
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.CallExecutionType
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.NovaSigner
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningContext
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SigningMode
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.SubmissionHierarchy
|
||||
import io.novafoundation.nova.feature_account_api.data.signer.setSignerData
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.TransferMode
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.nativeTransfer
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.runtime.ext.Geneses
|
||||
import io.novafoundation.nova.runtime.ext.requireGenesisHash
|
||||
import io.novafoundation.nova.runtime.extrinsic.ExtrinsicBuilderFactory
|
||||
import io.novasama.substrate_sdk_android.extensions.fromHex
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getRuntime
|
||||
import io.novafoundation.nova.runtime.network.rpc.RpcCalls
|
||||
import io.novasama.substrate_sdk_android.encrypt.EncryptionType
|
||||
import io.novasama.substrate_sdk_android.encrypt.MultiChainEncryption
|
||||
import io.novasama.substrate_sdk_android.encrypt.SignatureWrapper
|
||||
import io.novasama.substrate_sdk_android.encrypt.keypair.Keypair
|
||||
import io.novasama.substrate_sdk_android.encrypt.keypair.substrate.SubstrateKeypairFactory
|
||||
import io.novasama.substrate_sdk_android.encrypt.seed.substrate.SubstrateSeedFactory
|
||||
import io.novasama.substrate_sdk_android.runtime.AccountId
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.BatchMode
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.Nonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.builder.ExtrinsicBuilder
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.KeyPairSigner
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadRaw
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.InheritedImplication
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.CheckNonce.Companion.setNonce
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.GeneralTransactionSigner
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.verifySignature.VerifySignature.Companion.setVerifySignature
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.signingPayload
|
||||
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
|
||||
import io.novafoundation.nova.sr25519.BizinikiwSr25519
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* LIVE TRANSFER TEST - Transfers real HEZ tokens on Pezkuwi mainnet!
|
||||
*
|
||||
* Sender: 5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w
|
||||
* Recipient: 5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV
|
||||
* Amount: 5 HEZ
|
||||
*
|
||||
* Run with: ./gradlew :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=io.novafoundation.nova.PezkuwiLiveTransferTest
|
||||
*/
|
||||
class PezkuwiLiveTransferTest : BaseIntegrationTest() {
|
||||
|
||||
companion object {
|
||||
// Test wallet mnemonic
|
||||
private const val TEST_MNEMONIC = "crucial surge north silly divert throw habit fury zebra fabric tank output"
|
||||
|
||||
// Sender address (derived from mnemonic)
|
||||
private const val SENDER_ADDRESS = "5DXv3Dc5xELckTgcYa2dm1TSZPgqDPxVDW3Cid4ALWpVjY3w"
|
||||
|
||||
// Recipient address
|
||||
private const val RECIPIENT_ADDRESS = "5HdY6U2UQF8wPwczP3SoQz28kQu1WJSBqxKGePUKG4M5QYdV"
|
||||
|
||||
// Amount: 5 HEZ (with 12 decimals)
|
||||
private val TRANSFER_AMOUNT = BigInteger("5000000000000") // 5 * 10^12
|
||||
}
|
||||
|
||||
private val walletApi = FeatureUtils.getFeature<WalletFeatureApi>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
WalletFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val extrinsicBuilderFactory = runtimeApi.provideExtrinsicBuilderFactory()
|
||||
private val rpcCalls = runtimeApi.rpcCalls()
|
||||
|
||||
/**
|
||||
* LIVE TEST: Build and submit a real transfer on Pezkuwi mainnet
|
||||
*/
|
||||
@Test(timeout = 120000) // 2 minute timeout
|
||||
fun testLiveTransfer5HEZ() = runTest {
|
||||
Log.d("LiveTransferTest", "=== STARTING LIVE TRANSFER TEST ===")
|
||||
Log.d("LiveTransferTest", "Sender: $SENDER_ADDRESS")
|
||||
Log.d("LiveTransferTest", "Recipient: $RECIPIENT_ADDRESS")
|
||||
Log.d("LiveTransferTest", "Amount: 5 HEZ")
|
||||
|
||||
// Request full sync for Pezkuwi chain specifically
|
||||
Log.d("LiveTransferTest", "Requesting full sync for Pezkuwi chain...")
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
Log.d("LiveTransferTest", "Chain: ${chain.name}")
|
||||
|
||||
// Create keypair from mnemonic
|
||||
val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
|
||||
Log.d("LiveTransferTest", "Keypair created, public key: ${keypair.publicKey.toHexString()}")
|
||||
|
||||
// Create signer
|
||||
val signer = RealSigner(keypair, chain)
|
||||
Log.d("LiveTransferTest", "Signer created")
|
||||
|
||||
// Get recipient account ID
|
||||
val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()
|
||||
Log.d("LiveTransferTest", "Recipient AccountId: ${recipientAccountId.toHexString()}")
|
||||
|
||||
// Get current nonce using sender's SS58 address
|
||||
val nonce = try {
|
||||
rpcCalls.getNonce(chain.id, SENDER_ADDRESS)
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "Failed to get nonce, using 0", e)
|
||||
BigInteger.ZERO
|
||||
}
|
||||
Log.d("LiveTransferTest", "Current nonce: $nonce")
|
||||
|
||||
// Create extrinsic builder
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
Log.d("LiveTransferTest", "ExtrinsicBuilder created")
|
||||
|
||||
// Use default MORTAL era (same as @pezkuwi/api)
|
||||
Log.d("LiveTransferTest", "Using MORTAL era (default, same as @pezkuwi/api)")
|
||||
|
||||
// Add transfer call with KEEP_ALIVE mode (same as @pezkuwi/api uses)
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT, mode = TransferMode.KEEP_ALIVE)
|
||||
Log.d("LiveTransferTest", "Transfer call added")
|
||||
|
||||
// Set signer data for SUBMISSION (this is where TypeReference errors occur!)
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(RealSigningContext(chain, nonce), SigningMode.SUBMISSION)
|
||||
}
|
||||
Log.d("LiveTransferTest", "Signer data set successfully")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to set signer data!", e)
|
||||
fail("Failed to set signer data: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
return@runTest
|
||||
}
|
||||
|
||||
// Build the extrinsic
|
||||
val extrinsic = try {
|
||||
builder.buildExtrinsic()
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to build extrinsic!", e)
|
||||
fail("Failed to build extrinsic: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
return@runTest
|
||||
}
|
||||
|
||||
assertNotNull("Extrinsic should not be null", extrinsic)
|
||||
Log.d("LiveTransferTest", "Extrinsic built: ${extrinsic.extrinsicHex}")
|
||||
|
||||
// Submit the extrinsic
|
||||
Log.d("LiveTransferTest", "Submitting extrinsic to network...")
|
||||
try {
|
||||
val hash = rpcCalls.submitExtrinsic(chain.id, extrinsic)
|
||||
Log.d("LiveTransferTest", "=== TRANSFER SUBMITTED SUCCESSFULLY ===")
|
||||
Log.d("LiveTransferTest", "Transaction hash: $hash")
|
||||
println("LIVE TRANSFER SUCCESS! TX Hash: $hash")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED to submit extrinsic!", e)
|
||||
fail("Failed to submit extrinsic: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check type resolution in the runtime
|
||||
*/
|
||||
@Test(timeout = 120000)
|
||||
fun testTypeResolution() = runTest {
|
||||
Log.d("LiveTransferTest", "=== TESTING TYPE RESOLUTION ===")
|
||||
|
||||
// Request full sync for Pezkuwi chain
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
|
||||
// Check critical types for extrinsic encoding
|
||||
val typesToCheck = listOf(
|
||||
"Address",
|
||||
"MultiAddress",
|
||||
"GenericMultiAddress",
|
||||
"ExtrinsicSignature",
|
||||
"MultiSignature",
|
||||
"pezsp_runtime::multiaddress::MultiAddress",
|
||||
"pezsp_runtime::MultiSignature",
|
||||
"pezsp_runtime.multiaddress.MultiAddress",
|
||||
"pezsp_runtime.MultiSignature",
|
||||
"GenericExtrinsic",
|
||||
"Extrinsic"
|
||||
)
|
||||
|
||||
val results = mutableListOf<String>()
|
||||
for (typeName in typesToCheck) {
|
||||
val type = runtime.typeRegistry[typeName]
|
||||
val resolved = type?.let {
|
||||
try {
|
||||
// Try to get the actual type, not just alias
|
||||
it.toString()
|
||||
} catch (e: Exception) {
|
||||
"ERROR: ${e.message}"
|
||||
}
|
||||
}
|
||||
val status = if (type != null) "FOUND: $resolved" else "MISSING"
|
||||
results.add(" $typeName: $status")
|
||||
Log.d("LiveTransferTest", "$typeName: $status")
|
||||
}
|
||||
|
||||
// Check if extrinsic signature type is defined in metadata
|
||||
val extrinsicMeta = runtime.metadata.extrinsic
|
||||
Log.d("LiveTransferTest", "Extrinsic version: ${extrinsicMeta.version}")
|
||||
Log.d("LiveTransferTest", "Signed extensions: ${extrinsicMeta.signedExtensions.map { it.id }}")
|
||||
|
||||
// Log signed extension IDs
|
||||
for (ext in extrinsicMeta.signedExtensions) {
|
||||
Log.d("LiveTransferTest", "Extension: ${ext.id}")
|
||||
}
|
||||
|
||||
// Just log the extension names - type access might be restricted
|
||||
Log.d("LiveTransferTest", "Signed extensions count: ${extrinsicMeta.signedExtensions.size}")
|
||||
|
||||
// Log the extrinsic address type if available
|
||||
Log.d("LiveTransferTest", "RuntimeFactory diagnostics: ${io.novafoundation.nova.runtime.multiNetwork.runtime.RuntimeFactory.lastDiagnostics}")
|
||||
|
||||
println("Type resolution results:\n${results.joinToString("\n")}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fee calculation (doesn't submit, just builds for fee estimation)
|
||||
*/
|
||||
@Test(timeout = 120000)
|
||||
fun testFeeCalculation() = runTest {
|
||||
Log.d("LiveTransferTest", "=== TESTING FEE CALCULATION ===")
|
||||
|
||||
// Request full sync for Pezkuwi chain
|
||||
chainRegistry.enableFullSync(Chain.Geneses.PEZKUWI)
|
||||
val chain = chainRegistry.getChain(Chain.Geneses.PEZKUWI)
|
||||
|
||||
// First, log type registry state
|
||||
val runtime = chainRegistry.getRuntime(chain.id)
|
||||
Log.d("LiveTransferTest", "TypeRegistry has ExtrinsicSignature: ${runtime.typeRegistry["ExtrinsicSignature"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has MultiSignature: ${runtime.typeRegistry["MultiSignature"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has Address: ${runtime.typeRegistry["Address"] != null}")
|
||||
Log.d("LiveTransferTest", "TypeRegistry has MultiAddress: ${runtime.typeRegistry["MultiAddress"] != null}")
|
||||
|
||||
val keypair = createKeypairFromMnemonic(TEST_MNEMONIC)
|
||||
val signer = RealSigner(keypair, chain)
|
||||
val recipientAccountId = RECIPIENT_ADDRESS.toAccountId()
|
||||
|
||||
val builder = extrinsicBuilderFactory.create(
|
||||
chain = chain,
|
||||
options = ExtrinsicBuilderFactory.Options(BatchMode.BATCH)
|
||||
)
|
||||
|
||||
builder.nativeTransfer(accountId = recipientAccountId, amount = TRANSFER_AMOUNT)
|
||||
|
||||
// Set signer data for FEE mode
|
||||
try {
|
||||
with(builder) {
|
||||
signer.setSignerData(RealSigningContext(chain, BigInteger.ZERO), SigningMode.FEE)
|
||||
}
|
||||
Log.d("LiveTransferTest", "Signer data set, building extrinsic...")
|
||||
val extrinsic = builder.buildExtrinsic()
|
||||
assertNotNull("Fee extrinsic should not be null", extrinsic)
|
||||
Log.d("LiveTransferTest", "Extrinsic built, getting hex...")
|
||||
|
||||
// The error happens when accessing extrinsicHex
|
||||
try {
|
||||
val hex = extrinsic.extrinsicHex
|
||||
Log.d("LiveTransferTest", "Fee extrinsic built: $hex")
|
||||
println("Fee calculation test PASSED!")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "FAILED accessing extrinsicHex!", e)
|
||||
fail("Failed to get extrinsic hex: ${e.message}\nCause: ${e.cause?.message}\nStack: ${e.stackTraceToString()}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveTransferTest", "Fee calculation FAILED!", e)
|
||||
fail("Fee calculation failed: ${e.message}\nCause: ${e.cause?.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to create keypair from mnemonic
|
||||
private fun createKeypairFromMnemonic(mnemonic: String): Keypair {
|
||||
val seedResult = SubstrateSeedFactory.deriveSeed32(mnemonic, password = null)
|
||||
return SubstrateKeypairFactory.generate(EncryptionType.SR25519, seedResult.seed)
|
||||
}
|
||||
|
||||
// Real signer using actual keypair with bizinikiwi context
|
||||
private inner class RealSigner(
|
||||
private val keypair: Keypair,
|
||||
private val chain: Chain
|
||||
) : NovaSigner, GeneralTransactionSigner {
|
||||
|
||||
val accountId: ByteArray = keypair.publicKey
|
||||
|
||||
// Generate proper 96-byte keypair using BizinikiwSr25519 native library
|
||||
// This gives us the correct 64-byte secret key format for signing
|
||||
private val bizinikiwKeypair: ByteArray by lazy {
|
||||
val seedResult = SubstrateSeedFactory.deriveSeed32(TEST_MNEMONIC, password = null)
|
||||
BizinikiwSr25519.keypairFromSeed(seedResult.seed)
|
||||
}
|
||||
|
||||
// Extract 64-byte secret key (32-byte scalar + 32-byte nonce)
|
||||
private val bizinikiwSecretKey: ByteArray by lazy {
|
||||
BizinikiwSr25519.secretKeyFromKeypair(bizinikiwKeypair)
|
||||
}
|
||||
|
||||
// Extract 32-byte public key
|
||||
private val bizinikiwPublicKey: ByteArray by lazy {
|
||||
BizinikiwSr25519.publicKeyFromKeypair(bizinikiwKeypair)
|
||||
}
|
||||
|
||||
private val keyPairSigner = KeyPairSigner(
|
||||
keypair,
|
||||
MultiChainEncryption.Substrate(EncryptionType.SR25519)
|
||||
)
|
||||
|
||||
override suspend fun callExecutionType(): CallExecutionType {
|
||||
return CallExecutionType.IMMEDIATE
|
||||
}
|
||||
|
||||
override val metaAccount: MetaAccount = DefaultMetaAccount(
|
||||
id = 0,
|
||||
globallyUniqueId = "test-wallet",
|
||||
substrateAccountId = accountId,
|
||||
substrateCryptoType = null,
|
||||
substratePublicKey = keypair.publicKey,
|
||||
ethereumAddress = null,
|
||||
ethereumPublicKey = null,
|
||||
isSelected = true,
|
||||
name = "Test Wallet",
|
||||
type = LightMetaAccount.Type.SECRETS,
|
||||
chainAccounts = emptyMap(),
|
||||
status = LightMetaAccount.Status.ACTIVE,
|
||||
parentMetaId = null
|
||||
)
|
||||
|
||||
override suspend fun getSigningHierarchy(): SubmissionHierarchy {
|
||||
return SubmissionHierarchy(metaAccount, CallExecutionType.IMMEDIATE)
|
||||
}
|
||||
|
||||
override suspend fun signRaw(payload: SignerPayloadRaw): SignedRaw {
|
||||
return keyPairSigner.signRaw(payload)
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForSubmission(context: SigningContext) {
|
||||
val nonce = context.getNonce(AccountIdKey(accountId))
|
||||
setNonce(nonce)
|
||||
setVerifySignature(this@RealSigner, accountId)
|
||||
}
|
||||
|
||||
context(ExtrinsicBuilder)
|
||||
override suspend fun setSignerDataForFee(context: SigningContext) {
|
||||
setSignerDataForSubmission(context)
|
||||
}
|
||||
|
||||
override suspend fun submissionSignerAccountId(chain: Chain): AccountId {
|
||||
return accountId
|
||||
}
|
||||
|
||||
override suspend fun maxCallsPerTransaction(): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun signInheritedImplication(
|
||||
inheritedImplication: InheritedImplication,
|
||||
accountId: AccountId
|
||||
): SignatureWrapper {
|
||||
// Get the SDK's signing payload (SCALE format - same as @pezkuwi/api)
|
||||
val sdkPayloadBytes = inheritedImplication.signingPayload()
|
||||
|
||||
Log.d("LiveTransferTest", "=== SIGNING PAYLOAD (SDK - SCALE) ===")
|
||||
Log.d("LiveTransferTest", "SDK Payload hex: ${sdkPayloadBytes.toHexString()}")
|
||||
Log.d("LiveTransferTest", "SDK Payload length: ${sdkPayloadBytes.size} bytes")
|
||||
|
||||
// Debug: show first bytes to verify format
|
||||
if (sdkPayloadBytes.size >= 42) {
|
||||
val callData = sdkPayloadBytes.copyOfRange(0, 42)
|
||||
val extensions = sdkPayloadBytes.copyOfRange(42, sdkPayloadBytes.size)
|
||||
Log.d("LiveTransferTest", "Call data (42 bytes): ${callData.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Extensions (${extensions.size} bytes): ${extensions.toHexString()}")
|
||||
}
|
||||
|
||||
// Use BizinikiwSr25519 native library with "bizinikiwi" signing context
|
||||
Log.d("LiveTransferTest", "=== USING BIZINIKIWI CONTEXT ===")
|
||||
Log.d("LiveTransferTest", "Bizinikiwi public key: ${bizinikiwPublicKey.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Bizinikiwi secret key size: ${bizinikiwSecretKey.size} bytes")
|
||||
|
||||
val signatureBytes = BizinikiwSr25519.sign(
|
||||
publicKey = bizinikiwPublicKey,
|
||||
secretKey = bizinikiwSecretKey,
|
||||
message = sdkPayloadBytes
|
||||
)
|
||||
|
||||
Log.d("LiveTransferTest", "=== SIGNATURE PRODUCED ===")
|
||||
Log.d("LiveTransferTest", "Signature bytes: ${signatureBytes.toHexString()}")
|
||||
Log.d("LiveTransferTest", "Signature length: ${signatureBytes.size} bytes")
|
||||
|
||||
// Verify the signature locally before sending
|
||||
val verifyResult = BizinikiwSr25519.verify(signatureBytes, sdkPayloadBytes, bizinikiwPublicKey)
|
||||
Log.d("LiveTransferTest", "Local verification: $verifyResult")
|
||||
|
||||
return SignatureWrapper.Sr25519(signatureBytes)
|
||||
}
|
||||
}
|
||||
|
||||
private class RealSigningContext(
|
||||
override val chain: Chain,
|
||||
private val nonceValue: BigInteger
|
||||
) : SigningContext {
|
||||
override suspend fun getNonce(accountId: AccountIdKey): Nonce {
|
||||
return Nonce.ZERO + nonceValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.toHexString(): String {
|
||||
return joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.domain.ExtendedLoadingState
|
||||
import io.novafoundation.nova.common.utils.inBackground
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.AggregatedStakingDashboardOption
|
||||
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.StakingDashboard
|
||||
import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.isSyncing
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import org.junit.Test
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class StakingDashboardIntegrationTest: BaseIntegrationTest() {
|
||||
|
||||
private val stakingApi = FeatureUtils.getFeature<StakingFeatureApi>(context, StakingFeatureApi::class.java)
|
||||
|
||||
private val interactor = stakingApi.dashboardInteractor
|
||||
|
||||
private val updateSystem = stakingApi.dashboardUpdateSystem
|
||||
|
||||
private val gson = GsonBuilder()
|
||||
.registerTypeHierarchyAdapter(AggregatedStakingDashboardOption::class.java, AggregatedStakingDashboardOptionDesirializer())
|
||||
.create()
|
||||
|
||||
@Test
|
||||
fun syncStakingDashboard() = runTest {
|
||||
updateSystem.start()
|
||||
.inBackground()
|
||||
.launchIn(this)
|
||||
|
||||
interactor.stakingDashboardFlow()
|
||||
.inBackground()
|
||||
.collect(::logDashboard)
|
||||
}
|
||||
|
||||
private fun logDashboard(dashboard: ExtendedLoadingState<StakingDashboard>) {
|
||||
if (dashboard !is ExtendedLoadingState.Loaded) return
|
||||
|
||||
val serialized = gson.toJson(dashboard)
|
||||
|
||||
val message = """
|
||||
Dashboard state:
|
||||
Syncing items: ${dashboard.data.syncingItemsCount()}
|
||||
$serialized
|
||||
""".trimIndent()
|
||||
|
||||
Log.d("StakingDashboardIntegrationTest", message)
|
||||
}
|
||||
|
||||
private class AggregatedStakingDashboardOptionDesirializer : JsonSerializer<AggregatedStakingDashboardOption<*>> {
|
||||
override fun serialize(src: AggregatedStakingDashboardOption<*>, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonObject().apply {
|
||||
add("chain", JsonPrimitive(src.chain.name))
|
||||
add("stakingState", context.serialize(src.stakingState))
|
||||
add("syncing", context.serialize(src.syncingStage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun StakingDashboard.syncingItemsCount(): Int {
|
||||
return withoutStake.count { it.syncingStage.isSyncing() } + hasStake.count { it.syncingStage.isSyncing() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.LOG_TAG
|
||||
import io.novafoundation.nova.feature_staking_api.data.parachainStaking.turing.repository.AutomationAction
|
||||
import io.novafoundation.nova.feature_staking_api.data.parachainStaking.turing.repository.OptimalAutomationRequest
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.findChain
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import java.math.BigInteger
|
||||
|
||||
class TuringAutomationIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
private val stakingApi = FeatureUtils.getFeature<StakingFeatureApi>(context, StakingFeatureApi::class.java)
|
||||
private val automationTasksRepository = stakingApi.turingAutomationRepository
|
||||
|
||||
@Test
|
||||
fun calculateOptimalAutoCompounding(){
|
||||
runBlocking {
|
||||
val chain = chainRegistry.findTuringChain()
|
||||
val request = OptimalAutomationRequest(
|
||||
collator = "6AEG2WKRVvZteWWT3aMkk2ZE21FvURqiJkYpXimukub8Zb9C",
|
||||
amount = BigInteger("1000000000000")
|
||||
)
|
||||
|
||||
val response = automationTasksRepository.calculateOptimalAutomation(chain.id, request)
|
||||
|
||||
Log.d(LOG_TAG, response.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun calculateAutoCompoundExecutionFees(){
|
||||
runBlocking {
|
||||
val chain = chainRegistry.findTuringChain()
|
||||
val fees = automationTasksRepository.getTimeAutomationFees(chain.id, AutomationAction.AUTO_COMPOUND_DELEGATED_STAKE, executions = 1)
|
||||
|
||||
Log.d(LOG_TAG, fees.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun ChainRegistry.findTuringChain() = findChain { it.name == "Turing" }!!
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package io.novafoundation.nova
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.utils.LOG_TAG
|
||||
import io.novafoundation.nova.common.utils.second
|
||||
import io.novafoundation.nova.core.ethereum.Web3Api
|
||||
import io.novafoundation.nova.core.ethereum.log.Topic
|
||||
import io.novafoundation.nova.feature_wallet_api.data.network.blockhain.types.Balance
|
||||
import io.novafoundation.nova.runtime.ethereum.contract.base.querySingle
|
||||
import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Queries
|
||||
import io.novafoundation.nova.runtime.ethereum.contract.erc20.Erc20Standard
|
||||
import io.novafoundation.nova.runtime.ethereum.sendSuspend
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getEthereumApiOrThrow
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.reactive.asFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.web3j.abi.EventEncoder
|
||||
import org.web3j.abi.TypeEncoder
|
||||
import org.web3j.abi.datatypes.Address
|
||||
import org.web3j.protocol.core.DefaultBlockParameterName
|
||||
import java.math.BigInteger
|
||||
|
||||
class Erc20Transfer(
|
||||
val txHash: String,
|
||||
val blockNumber: String,
|
||||
val from: String,
|
||||
val to: String,
|
||||
val contract: String,
|
||||
val amount: BigInteger,
|
||||
)
|
||||
|
||||
class Web3jServiceIntegrationTest : BaseIntegrationTest() {
|
||||
|
||||
@Test
|
||||
fun shouldFetchBalance(): Unit = runBlocking {
|
||||
val web3j = moonbeamWeb3j()
|
||||
val balance = web3j.ethGetBalance("0xf977814e90da44bfa03b6295a0616a897441acec", DefaultBlockParameterName.LATEST).sendSuspend()
|
||||
Log.d(LOG_TAG, balance.balance.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldFetchComplexStructure(): Unit = runBlocking {
|
||||
val web3j = moonbeamWeb3j()
|
||||
val block = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, true).sendSuspend()
|
||||
Log.d(LOG_TAG, block.block.hash)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSubscribeToNewHeadEvents(): Unit = runBlocking {
|
||||
val web3j = moonbeamWeb3j()
|
||||
val newHead = web3j.newHeadsNotifications().asFlow().first()
|
||||
|
||||
Log.d(LOG_TAG, "New head appended to chain: ${newHead.params.result.hash}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldSubscribeBalances(): Unit = runBlocking {
|
||||
val web3j = moonbeamWeb3j()
|
||||
val accountAddress = "0x4A43C16107591AE5Ec904e584ed4Bb05386F98f7"
|
||||
val moonbeamUsdc = "0x818ec0a7fe18ff94269904fced6ae3dae6d6dc0b"
|
||||
|
||||
val balanceUpdates = web3j.erc20BalanceFlow(accountAddress, moonbeamUsdc).take(2).toList()
|
||||
|
||||
error("Initial balance: ${balanceUpdates.first()}, new balance: ${balanceUpdates.second()}")
|
||||
}
|
||||
|
||||
private fun Web3Api.erc20BalanceFlow(account: String, contract: String): Flow<Balance> {
|
||||
return flow {
|
||||
val erc20 = Erc20Standard().querySingle(contract, web3j = this@erc20BalanceFlow)
|
||||
val initialBalance = erc20.balanceOfAsync(account).await()
|
||||
|
||||
emit(initialBalance)
|
||||
|
||||
val changes = accountErcTransfersFlow(account).map {
|
||||
erc20.balanceOfAsync(account).await()
|
||||
}
|
||||
|
||||
emitAll(changes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun Web3Api.accountErcTransfersFlow(address: String): Flow<Erc20Transfer> {
|
||||
val addressTopic = TypeEncoder.encode(Address(address))
|
||||
|
||||
val transferEvent = Erc20Queries.TRANSFER_EVENT
|
||||
val transferEventSignature = EventEncoder.encode(transferEvent)
|
||||
val contractAddresses = emptyList<String>() // everything
|
||||
|
||||
val erc20SendTopic = listOf(
|
||||
Topic.Single(transferEventSignature), // zero-th topic is event signature
|
||||
Topic.AnyOf(addressTopic), // our account as `from`
|
||||
)
|
||||
|
||||
val erc20ReceiveTopic = listOf(
|
||||
Topic.Single(transferEventSignature), // zero-th topic is event signature
|
||||
Topic.Any, // anyone is `from`
|
||||
Topic.AnyOf(addressTopic) // out account as `to`
|
||||
)
|
||||
|
||||
val receiveTransferNotifications = logsNotifications(contractAddresses, erc20ReceiveTopic)
|
||||
val sendTransferNotifications = logsNotifications(contractAddresses, erc20SendTopic)
|
||||
|
||||
val transferNotifications = merge(receiveTransferNotifications, sendTransferNotifications)
|
||||
|
||||
return transferNotifications.map { logNotification ->
|
||||
val log = logNotification.params.result
|
||||
|
||||
val contract = log.address
|
||||
val event = Erc20Queries.parseTransferEvent(log)
|
||||
|
||||
Erc20Transfer(
|
||||
txHash = log.transactionHash,
|
||||
blockNumber = log.blockNumber,
|
||||
from = event.from.value,
|
||||
to = event.to.value,
|
||||
contract = contract,
|
||||
amount = event.amount.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun moonbeamWeb3j(): Web3Api {
|
||||
val moonbeamChainId = "fe58ea77779b7abda7da4ec526d14db9b1e9cd40a217c34892af80a9b332b76d"
|
||||
|
||||
return chainRegistry.getEthereumApiOrThrow(moonbeamChainId, Chain.Node.ConnectionType.WSS)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package io.novafoundation.nova.balances
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.google.gson.Gson
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.AccountInfo
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindAccountInfo
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.fromJson
|
||||
import io.novafoundation.nova.common.utils.hasModule
|
||||
import io.novafoundation.nova.common.utils.system
|
||||
import io.novafoundation.nova.core.model.CryptoType
|
||||
import io.novafoundation.nova.core_db.model.chain.account.MetaAccountLocal
|
||||
import io.novafoundation.nova.feature_account_api.data.ethereum.transaction.TransactionOrigin
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.LightMetaAccount
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.MetaAccount
|
||||
import io.novafoundation.nova.feature_account_impl.di.AccountFeatureComponent
|
||||
import io.novafoundation.nova.feature_account_impl.domain.account.model.DefaultMetaAccount
|
||||
import io.novafoundation.nova.runtime.BuildConfig.TEST_CHAINS_URL
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeComponent
|
||||
import io.novafoundation.nova.runtime.extrinsic.systemRemark
|
||||
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import io.novafoundation.nova.runtime.multiNetwork.getSocket
|
||||
import io.novasama.substrate_sdk_android.extensions.fromHex
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storage
|
||||
import io.novasama.substrate_sdk_android.runtime.metadata.storageKey
|
||||
import io.novasama.substrate_sdk_android.wsrpc.networkStateFlow
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.math.BigInteger
|
||||
import java.math.BigInteger.ZERO
|
||||
import java.net.URL
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class BalancesIntegrationTest(
|
||||
private val testChainId: String,
|
||||
private val testChainName: String,
|
||||
private val testAccount: String
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{1}")
|
||||
fun data(): List<Array<String?>> {
|
||||
val arrayOfNetworks: Array<TestData> = Gson().fromJson(URL(TEST_CHAINS_URL).readText())
|
||||
return arrayOfNetworks.map { arrayOf(it.chainId, it.name, it.account) }
|
||||
}
|
||||
|
||||
class TestData(
|
||||
val chainId: String,
|
||||
val name: String,
|
||||
val account: String?
|
||||
)
|
||||
}
|
||||
|
||||
private val maxAmount = BigInteger.valueOf(10).pow(30)
|
||||
|
||||
private val runtimeApi = FeatureUtils.getFeature<RuntimeComponent>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
RuntimeApi::class.java
|
||||
)
|
||||
|
||||
private val accountApi = FeatureUtils.getFeature<AccountFeatureComponent>(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
AccountFeatureApi::class.java
|
||||
)
|
||||
|
||||
private val chainRegistry = runtimeApi.chainRegistry()
|
||||
private val externalRequirementFlow = runtimeApi.externalRequirementFlow()
|
||||
|
||||
private val remoteStorage = runtimeApi.remoteStorageSource()
|
||||
|
||||
private val extrinsicService = accountApi.extrinsicService()
|
||||
|
||||
@Before
|
||||
fun before() = runBlocking {
|
||||
externalRequirementFlow.emit(ChainConnection.ExternalRequirement.ALLOWED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalancesLoading() = runBlocking(Dispatchers.Default) {
|
||||
val chains = chainRegistry.getChain(testChainId)
|
||||
|
||||
val freeBalance = testBalancesInChainAsync(chains, testAccount)?.data?.free ?: error("Balance was null")
|
||||
|
||||
assertTrue("Free balance: $freeBalance is less than $maxAmount", maxAmount > freeBalance)
|
||||
assertTrue("Free balance: $freeBalance is greater than 0", ZERO < freeBalance)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFeeLoading() = runBlocking(Dispatchers.Default) {
|
||||
val chains = chainRegistry.getChain(testChainId)
|
||||
|
||||
testFeeLoadingAsync(chains)
|
||||
|
||||
Unit
|
||||
}
|
||||
|
||||
private suspend fun testBalancesInChainAsync(chain: Chain, currentAccount: String): AccountInfo? {
|
||||
return coroutineScope {
|
||||
try {
|
||||
withTimeout(80.seconds) {
|
||||
remoteStorage.query(
|
||||
chainId = chain.id,
|
||||
keyBuilder = { it.metadata.system().storage("Account").storageKey(it, currentAccount.fromHex()) },
|
||||
binding = { scale, runtime -> scale?.let { bindAccountInfo(scale, runtime) } }
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw Exception("Socket state: ${chainRegistry.getSocket(chain.id).networkStateFlow().first()}, error: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testFeeLoadingAsync(chain: Chain) {
|
||||
return coroutineScope {
|
||||
withTimeout(80.seconds) {
|
||||
extrinsicService.estimateFee(chain, testTransactionOrigin()) {
|
||||
systemRemark(byteArrayOf(0))
|
||||
|
||||
val haveBatch = runtime.metadata.hasModule("Utility")
|
||||
if (haveBatch) {
|
||||
systemRemark(byteArrayOf(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun testTransactionOrigin(): TransactionOrigin = TransactionOrigin.Wallet(
|
||||
createTestMetaAccount()
|
||||
)
|
||||
|
||||
private fun createTestMetaAccount(): MetaAccount {
|
||||
val metaAccount = DefaultMetaAccount(
|
||||
id = 0,
|
||||
globallyUniqueId = MetaAccountLocal.generateGloballyUniqueId(),
|
||||
substratePublicKey = testAccount.fromHex(),
|
||||
substrateCryptoType = CryptoType.SR25519,
|
||||
substrateAccountId = testAccount.fromHex(),
|
||||
ethereumAddress = testAccount.fromHex(),
|
||||
ethereumPublicKey = testAccount.fromHex(),
|
||||
isSelected = true,
|
||||
name = "Test",
|
||||
type = LightMetaAccount.Type.WATCH_ONLY,
|
||||
status = LightMetaAccount.Status.ACTIVE,
|
||||
chainAccounts = emptyMap(),
|
||||
parentMetaId = null
|
||||
)
|
||||
return metaAccount
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">[Debug] Pezkuwi</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">[Dev] Pezkuwi</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,234 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-permission
|
||||
android:name="com.google.android.gms.permission.AD_ID"
|
||||
tools:node="remove" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
tools:targetApi="s" />
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:name="io.novafoundation.nova.app.App"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
tools:replace="android:allowBackup,android:fullBackupContent,android:dataExtractionRules"
|
||||
tools:targetApi="s">
|
||||
|
||||
<activity
|
||||
android:name="io.novafoundation.nova.app.root.presentation.RootActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.NovaFoundation.Nova"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<intent-filter tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/json" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<data
|
||||
android:host="buy-success"
|
||||
android:scheme="pezkuwi" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<data
|
||||
android:host="@string/deep_linking_host"
|
||||
android:scheme="@string/deep_linking_scheme" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<data android:host="request" />
|
||||
<data android:scheme="pezkuwiwallet" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<data android:host="wc" />
|
||||
<data android:scheme="pezkuwiwallet" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<data
|
||||
android:pathPattern="/.*@2"
|
||||
android:scheme="wc" />
|
||||
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="app.pezkuwichain.io" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https"/>
|
||||
<data android:host="@string/branch_io_link_host"/>
|
||||
<data android:host="@string/branch_io_link_host_alternate"/>
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:theme="@style/Theme.NovaFoundation.Nova"
|
||||
tools:replace="android:theme,screenOrientation" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name="io.novafoundation.nova.feature_account_impl.presentation.exporting.json.ShareCompletedReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name="io.novafoundation.nova.feature_ledger_impl.sdk.connection.usb.UsbLedgerConnection$UsbPermissionReceiver"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BROADCAST_USB" />
|
||||
|
||||
<service
|
||||
android:name="io.novafoundation.nova.feature_push_notifications.NovaFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_pezkuwi" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/android_system_accent" />
|
||||
|
||||
<meta-data
|
||||
android:name="firebase_messaging_auto_init_enabled"
|
||||
android:value="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="@string/default_notification_channel_id" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.novafoundation.nova.transactions_notification_channel_id"
|
||||
android:value="@string/transactions_notification_channel_id" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.novafoundation.nova.governance_notification_channel_id"
|
||||
android:value="@string/governance_notification_channel_id" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.novafoundation.nova.staking_notification_channel_id"
|
||||
android:value="@string/staking_notification_channel_id" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.novafoundation.nova.multisigs_notification_channel_id"
|
||||
android:value="@string/multisigs_notification_channel_id" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.branch.sdk.BranchKey"
|
||||
android:value="${BRANCH_KEY}" />
|
||||
|
||||
<meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
|
||||
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent>
|
||||
|
||||
<!-- Allow Google Pay feature in WebView -->
|
||||
<intent>
|
||||
<action android:name="org.chromium.intent.action.PAY" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.chromium.intent.action.IS_READY_TO_PAY" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="org.chromium.intent.action.UPDATE_PAYMENT_DETAILS" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.novafoundation.nova.app
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import com.walletconnect.android.Core
|
||||
import com.walletconnect.android.CoreClient
|
||||
import com.walletconnect.android.relay.ConnectionType
|
||||
import com.walletconnect.web3.wallet.client.Wallet
|
||||
import com.walletconnect.web3.wallet.client.Web3Wallet
|
||||
import io.novafoundation.nova.app.di.app.AppComponent
|
||||
import io.novafoundation.nova.app.di.deps.FeatureHolderManager
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.common.resources.LanguagesHolder
|
||||
import io.novafoundation.nova.common.utils.coroutines.RootScope
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIOLinkHandler
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.BuildConfig
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val WC_REDIRECT_URL = "pezkuwiwallet://request"
|
||||
|
||||
open class App : Application(), FeatureContainer {
|
||||
|
||||
@Inject
|
||||
lateinit var featureHolderManager: FeatureHolderManager
|
||||
|
||||
private lateinit var appComponent: AppComponent
|
||||
|
||||
private val languagesHolder: LanguagesHolder = LanguagesHolder()
|
||||
|
||||
// App global scope using for processes that should work while app is alive
|
||||
private val rootScope = RootScope()
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
val contextManager = ContextManager.getInstanceOrInit(base, languagesHolder)
|
||||
super.attachBaseContext(contextManager.setLocale(base))
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
val contextManager = ContextManager.getInstanceOrInit(this, languagesHolder)
|
||||
contextManager.setLocale(this)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
val contextManger = ContextManager.getInstanceOrInit(this, languagesHolder)
|
||||
|
||||
appComponent = io.novafoundation.nova.app.di.app.DaggerAppComponent
|
||||
.builder()
|
||||
.application(this)
|
||||
.contextManager(contextManger)
|
||||
.rootScope(rootScope)
|
||||
.build()
|
||||
|
||||
appComponent.inject(this)
|
||||
|
||||
BranchIOLinkHandler.Initializer.init(this)
|
||||
|
||||
initializeWalletConnect()
|
||||
}
|
||||
|
||||
override fun <T> getFeature(key: Class<*>): T {
|
||||
return featureHolderManager.getFeature<T>(key)!!
|
||||
}
|
||||
|
||||
override fun releaseFeature(key: Class<*>) {
|
||||
featureHolderManager.releaseFeature(key)
|
||||
}
|
||||
|
||||
override fun commonApi(): CommonApi {
|
||||
return appComponent
|
||||
}
|
||||
|
||||
private fun initializeWalletConnect() {
|
||||
val projectId = BuildConfig.WALLET_CONNECT_PROJECT_ID
|
||||
val relayUrl = "relay.walletconnect.com"
|
||||
val serverUrl = "wss://$relayUrl?projectId=$projectId"
|
||||
val connectionType = ConnectionType.MANUAL
|
||||
val appMetaData = Core.Model.AppMetaData(
|
||||
name = "Pezkuwi Wallet",
|
||||
description = "Next-gen wallet for Pezkuwichain and Polkadot ecosystem",
|
||||
url = "https://pezkuwichain.io/",
|
||||
icons = listOf("https://raw.githubusercontent.com/pezkuwichain/branding/master/logos/Pezkuwi_Wallet_Sun_Color.png"),
|
||||
redirect = WC_REDIRECT_URL
|
||||
)
|
||||
|
||||
CoreClient.initialize(relayServerUrl = serverUrl, connectionType = connectionType, application = this, metaData = appMetaData) { error ->
|
||||
// TODO maybe re-initialize client
|
||||
}
|
||||
|
||||
val initParams = Wallet.Params.Init(core = CoreClient)
|
||||
|
||||
Web3Wallet.initialize(initParams) { error ->
|
||||
// TODO maybe re-initialize client
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.novafoundation.nova.app.di.app
|
||||
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.app.App
|
||||
import io.novafoundation.nova.app.di.app.navigation.NavigationModule
|
||||
import io.novafoundation.nova.app.di.deps.ComponentHolderModule
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.modules.CommonModule
|
||||
import io.novafoundation.nova.common.di.modules.NetworkModule
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.common.utils.coroutines.RootScope
|
||||
|
||||
@ApplicationScope
|
||||
@Component(
|
||||
modules = [
|
||||
AppModule::class,
|
||||
CommonModule::class,
|
||||
NetworkModule::class,
|
||||
NavigationModule::class,
|
||||
ComponentHolderModule::class,
|
||||
FeatureManagerModule::class
|
||||
]
|
||||
)
|
||||
interface AppComponent : CommonApi {
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
fun application(application: App): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun contextManager(contextManager: ContextManager): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun rootScope(rootScope: RootScope): Builder
|
||||
|
||||
fun build(): AppComponent
|
||||
}
|
||||
|
||||
fun inject(app: App)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.app.di.app
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.App
|
||||
import io.novafoundation.nova.app.root.presentation.common.RealBuildTypeProvider
|
||||
import io.novafoundation.nova.app.root.presentation.common.RootActivityIntentProvider
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.interfaces.ActivityIntentProvider
|
||||
import io.novafoundation.nova.common.interfaces.BuildTypeProvider
|
||||
|
||||
@Module
|
||||
class AppModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideContext(application: App): Context {
|
||||
return application
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRootActivityIntentProvider(context: Context): ActivityIntentProvider = RootActivityIntentProvider(context)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideBuildTypeProvider(): BuildTypeProvider = RealBuildTypeProvider()
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.app.di.app
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.di.deps.FeatureHolderManager
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
|
||||
@Module
|
||||
class FeatureManagerModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideFeatureHolderManager(featureApiHolderMap: @JvmSuppressWildcards Map<Class<*>, FeatureApiHolder>): FeatureHolderManager {
|
||||
return FeatureHolderManager(featureApiHolderMap)
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.accountmigration.AccountMigrationNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_account_migration.presentation.AccountMigrationRouter
|
||||
|
||||
@Module
|
||||
class AccountMigrationNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): AccountMigrationRouter = AccountMigrationNavigator(
|
||||
navigationHoldersRegistry = navigationHoldersRegistry
|
||||
)
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.PolkadotVaultVariantSignCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.ScanSeedCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectAddressCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectMultipleWalletsCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectSingleWalletCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.account.SelectWalletCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.ChangeBackupPasswordCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.RestoreBackupPasswordCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.SyncWalletsBackupPasswordCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.pincode.PinCodeTwoFactorVerificationCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.sequrity.verification.PinCodeTwoFactorVerificationCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectAddress.SelectAddressCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectMultipleWalletsCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.ChangeBackupPasswordCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.changePassword.RestoreBackupPasswordCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectWallet.SelectWalletCommunicator
|
||||
import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.PolkadotVaultVariantSignCommunicator
|
||||
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.cloudBackup.createPassword.SyncWalletsBackupPasswordCommunicator
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.mixin.selectSingleWallet.SelectSingleWalletCommunicator
|
||||
import io.novafoundation.nova.feature_account_impl.presentation.seedScan.ScanSeedCommunicator
|
||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||
|
||||
@Module
|
||||
class AccountNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePinCodeTwoFactorVerificationCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): PinCodeTwoFactorVerificationCommunicator = PinCodeTwoFactorVerificationCommunicatorImpl(navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectWalletCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SelectWalletCommunicator = SelectWalletCommunicatorImpl(navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideParitySignerCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): PolkadotVaultVariantSignCommunicator = PolkadotVaultVariantSignCommunicatorImpl(navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectAddressCommunicator(
|
||||
router: AssetsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SelectAddressCommunicator = SelectAddressCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideScanSeedCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): ScanSeedCommunicator = ScanSeedCommunicatorImpl(navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectSingleWalletCommunicator(
|
||||
router: AssetsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SelectSingleWalletCommunicator = SelectSingleWalletCommunicatorImpl(router)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectMultipleWalletsCommunicator(
|
||||
router: AssetsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SelectMultipleWalletsCommunicator = SelectMultipleWalletsCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideAccountRouter(navigator: Navigator): AccountRouter = navigator
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePushGovernanceSettingsCommunicator(
|
||||
router: AccountRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SyncWalletsBackupPasswordCommunicator = SyncWalletsBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideChangeBackupPasswordCommunicator(
|
||||
router: AccountRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): ChangeBackupPasswordCommunicator = ChangeBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRestoreBackupPasswordCommunicator(
|
||||
router: AccountRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): RestoreBackupPasswordCommunicator = RestoreBackupPasswordCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.topup.TopUpAddressCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_assets.presentation.topup.TopUpAddressCommunicator
|
||||
|
||||
@Module
|
||||
class AssetNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideTopUpAddressCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): TopUpAddressCommunicator {
|
||||
return TopUpAddressCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.buy.BuyNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_buy_impl.presentation.BuyRouter
|
||||
|
||||
@Module
|
||||
class BuyNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): BuyRouter =
|
||||
BuyNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.chainMigration.ChainMigrationNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_ahm_impl.presentation.ChainMigrationRouter
|
||||
|
||||
@Module
|
||||
class ChainMigrationNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): ChainMigrationRouter =
|
||||
ChainMigrationNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.cloudBackup.CloudBackupNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_cloud_backup_impl.presentation.CloudBackupRouter
|
||||
|
||||
@Module
|
||||
class CloudBackupNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): CloudBackupRouter =
|
||||
CloudBackupNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.wallet.CurrencyNavigator
|
||||
import io.novafoundation.nova.app.root.presentation.RootRouter
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_currency_api.presentation.CurrencyRouter
|
||||
|
||||
@Module
|
||||
class CurrencyNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
rootRouter: RootRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
): CurrencyRouter = CurrencyNavigator(rootRouter, navigationHoldersRegistry)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.dApp.DAppNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.dApp.DAppSearchCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCommunicator
|
||||
|
||||
@Module
|
||||
class DAppNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): DAppRouter = DAppNavigator(navigationHoldersRegistry)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideSearchDappCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): DAppSearchCommunicator {
|
||||
return DAppSearchCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.externalSign.ExternalSignCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.externalSign.ExternalSignNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.ExternalSignCommunicator
|
||||
import io.novafoundation.nova.feature_external_sign_impl.ExternalSignRouter
|
||||
|
||||
@Module
|
||||
class ExternalSignNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): ExternalSignRouter =
|
||||
ExternalSignNavigator(navigationHoldersRegistry)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideSignExtrinsicCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
automaticInteractionGate: AutomaticInteractionGate,
|
||||
): ExternalSignCommunicator {
|
||||
return ExternalSignCommunicatorImpl(navigationHoldersRegistry, automaticInteractionGate)
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.gift.GiftNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_gift_impl.presentation.GiftRouter
|
||||
|
||||
@Module
|
||||
class GiftNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(commonDelegate: Navigator, navigationHoldersRegistry: NavigationHoldersRegistry): GiftRouter =
|
||||
GiftNavigator(commonDelegate, navigationHoldersRegistry)
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.governance.GovernanceNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.governance.SelectTracksCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.governance.TinderGovVoteCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.list.SelectTracksCommunicator
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
|
||||
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
|
||||
import io.novafoundation.nova.feature_governance_impl.presentation.referenda.vote.setup.tindergov.TinderGovVoteCommunicator
|
||||
|
||||
@Module
|
||||
class GovernanceNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
commonNavigator: Navigator,
|
||||
contextManager: ContextManager,
|
||||
dAppRouter: DAppRouter
|
||||
): GovernanceRouter = GovernanceNavigator(navigationHoldersRegistry, commonNavigator, contextManager, dAppRouter)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectTracksCommunicator(
|
||||
router: GovernanceRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): SelectTracksCommunicator = SelectTracksCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideTinderGovVoteCommunicator(
|
||||
router: GovernanceRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): TinderGovVoteCommunicator = TinderGovVoteCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.ledger.LedgerNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.ledger.LedgerSignCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.ledger.SelectLedgerAddressCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.sign.LedgerSignCommunicator
|
||||
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
|
||||
import io.novafoundation.nova.feature_ledger_impl.presentation.LedgerRouter
|
||||
import io.novafoundation.nova.feature_ledger_impl.presentation.account.connect.legacy.SelectLedgerAddressInterScreenCommunicator
|
||||
|
||||
@Module
|
||||
class LedgerNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideSelectLedgerAddressCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectLedgerAddressInterScreenCommunicator {
|
||||
return SelectLedgerAddressCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideLedgerSignerCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): LedgerSignCommunicator = LedgerSignCommunicatorImpl(navigationHoldersRegistry)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(router: AccountRouter, navigationHoldersRegistry: NavigationHoldersRegistry): LedgerRouter =
|
||||
LedgerNavigator(router, navigationHoldersRegistry)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.multisig.MultisigOperationsNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_multisig_operations.presentation.MultisigOperationsRouter
|
||||
|
||||
@Module
|
||||
class MultisigNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideOperationsRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
commonDelegate: Navigator
|
||||
): MultisigOperationsRouter = MultisigOperationsNavigator(navigationHoldersRegistry, commonDelegate)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.di.app.navigation.staking.StakingNavigationModule
|
||||
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.presentation.RootRouter
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||
import io.novafoundation.nova.feature_crowdloan_impl.presentation.CrowdloanRouter
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
|
||||
import io.novafoundation.nova.splash.SplashRouter
|
||||
|
||||
@Module(
|
||||
includes = [
|
||||
AccountNavigationModule::class,
|
||||
AssetNavigationModule::class,
|
||||
DAppNavigationModule::class,
|
||||
NftNavigationModule::class,
|
||||
StakingNavigationModule::class,
|
||||
LedgerNavigationModule::class,
|
||||
CurrencyNavigationModule::class,
|
||||
GovernanceNavigationModule::class,
|
||||
WalletConnectNavigationModule::class,
|
||||
VoteNavigationModule::class,
|
||||
VersionsNavigationModule::class,
|
||||
ExternalSignNavigationModule::class,
|
||||
SettingsNavigationModule::class,
|
||||
SwapNavigationModule::class,
|
||||
BuyNavigationModule::class,
|
||||
PushNotificationsNavigationModule::class,
|
||||
CloudBackupNavigationModule::class,
|
||||
AssetNavigationModule::class,
|
||||
AccountMigrationNavigationModule::class,
|
||||
MultisigNavigationModule::class,
|
||||
ChainMigrationNavigationModule::class,
|
||||
WalletNavigationModule::class,
|
||||
GiftNavigationModule::class
|
||||
]
|
||||
)
|
||||
class NavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideMainNavigatorHolder(
|
||||
contextManager: ContextManager
|
||||
): SplitScreenNavigationHolder = SplitScreenNavigationHolder(contextManager)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideDappNavigatorHolder(
|
||||
contextManager: ContextManager
|
||||
): RootNavigationHolder = RootNavigationHolder(contextManager)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideNavigationHoldersRegistry(
|
||||
rootNavigatorHolder: RootNavigationHolder,
|
||||
splitScreenNavigationHolder: SplitScreenNavigationHolder,
|
||||
): NavigationHoldersRegistry {
|
||||
return NavigationHoldersRegistry(splitScreenNavigationHolder, rootNavigatorHolder)
|
||||
}
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideNavigator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
walletConnectRouter: WalletConnectRouter
|
||||
): Navigator = Navigator(navigationHoldersRegistry, walletConnectRouter)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRootRouter(navigator: Navigator): RootRouter = navigator
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideSplashRouter(navigator: Navigator): SplashRouter = navigator
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideOnboardingRouter(navigator: Navigator): OnboardingRouter = navigator
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideAssetsRouter(navigator: Navigator): AssetsRouter = navigator
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideCrowdloanRouter(navigator: Navigator): CrowdloanRouter = navigator
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideDelayedNavigationRouter(navigator: Navigator): DelayedNavigationRouter = navigator
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.nft.NftNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_nft_impl.NftRouter
|
||||
|
||||
@Module
|
||||
class NftNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): NftRouter =
|
||||
NftNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.push.PushGovernanceSettingsCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.push.PushMultisigSettingsCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.push.PushNotificationsNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.push.PushStakingSettingsCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_push_notifications.PushNotificationsRouter
|
||||
import io.novafoundation.nova.feature_push_notifications.presentation.governance.PushGovernanceSettingsCommunicator
|
||||
import io.novafoundation.nova.feature_push_notifications.presentation.multisigs.PushMultisigSettingsCommunicator
|
||||
import io.novafoundation.nova.feature_push_notifications.presentation.staking.PushStakingSettingsCommunicator
|
||||
|
||||
@Module
|
||||
class PushNotificationsNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): PushNotificationsRouter =
|
||||
PushNotificationsNavigator(navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePushGovernanceSettingsCommunicator(
|
||||
router: PushNotificationsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): PushGovernanceSettingsCommunicator = PushGovernanceSettingsCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePushStakingSettingsCommunicator(
|
||||
router: PushNotificationsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): PushStakingSettingsCommunicator = PushStakingSettingsCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun providePushMultisigSettingsCommunicator(
|
||||
router: PushNotificationsRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
): PushMultisigSettingsCommunicator = PushMultisigSettingsCommunicatorImpl(router, navigationHoldersRegistry)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.settings.SettingsNavigator
|
||||
import io.novafoundation.nova.app.root.presentation.RootRouter
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_settings_impl.SettingsRouter
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
|
||||
|
||||
@Module
|
||||
class SettingsNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
rootRouter: RootRouter,
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
walletConnectRouter: WalletConnectRouter,
|
||||
navigator: Navigator,
|
||||
): SettingsRouter = SettingsNavigator(navigationHoldersRegistry, rootRouter, walletConnectRouter, navigator)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.swap.SwapNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_swap_impl.presentation.SwapRouter
|
||||
|
||||
@Module
|
||||
class SwapNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
commonDelegate: Navigator
|
||||
): SwapRouter = SwapNavigator(navigationHoldersRegistry, commonDelegate)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.versions.VersionsNavigator
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.feature_versions_api.presentation.VersionsRouter
|
||||
|
||||
@Module
|
||||
class VersionsNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
contextManager: ContextManager,
|
||||
appLinksProvider: AppLinksProvider
|
||||
): VersionsRouter = VersionsNavigator(navigationHoldersRegistry, contextManager, appLinksProvider.storeUrl)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.vote.VoteNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
|
||||
|
||||
@Module
|
||||
class VoteNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideVoteRouter(navigator: Navigator): VoteRouter = VoteNavigator(navigator)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.walletConnect.ApproveSessionCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.walletConnect.WalletConnectNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.WalletConnectRouter
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.presentation.sessions.approve.ApproveSessionCommunicator
|
||||
|
||||
@Module
|
||||
class WalletConnectNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideApproveSessionCommunicator(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
automaticInteractionGate: AutomaticInteractionGate,
|
||||
): ApproveSessionCommunicator = ApproveSessionCommunicatorImpl(navigationHoldersRegistry, automaticInteractionGate)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry): WalletConnectRouter =
|
||||
WalletConnectNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.wallet.WalletNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_wallet_impl.presentation.WalletRouter
|
||||
|
||||
@Module
|
||||
class WalletNavigationModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun provideRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
commonDelegate: Navigator
|
||||
): WalletRouter = WalletNavigator(commonDelegate, navigationHoldersRegistry)
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation.staking
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.MythosStakingNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythCollatorSettingsInterScreenCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.mythos.SelectMythosCollatorInterScreenCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.MythosStakingRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.SelectMythosInterScreenCommunicator
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.mythos.start.selectCollatorSettings.SelectMythCollatorSettingsInterScreenCommunicator
|
||||
|
||||
@Module
|
||||
class MythosStakingNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideMythosStakingRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
stakingDashboardRouter: StakingDashboardRouter,
|
||||
): MythosStakingRouter {
|
||||
return MythosStakingNavigator(navigationHoldersRegistry, stakingDashboardRouter)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythosInterScreenCommunicator {
|
||||
return SelectMythosCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectSettingsCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectMythCollatorSettingsInterScreenCommunicator {
|
||||
return SelectMythCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation.staking
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.nominationPools.NominationPoolsStakingNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.NominationPoolsRouter
|
||||
|
||||
@Module
|
||||
class NominationPoolsStakingNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRouter(navigationHoldersRegistry: NavigationHoldersRegistry, navigator: Navigator): NominationPoolsRouter {
|
||||
return NominationPoolsStakingNavigator(navigationHoldersRegistry, navigator)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation.staking
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.ParachainStakingNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.SelectCollatorInterScreenCommunicatorImpl
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.parachain.SelectCollatorSettingsInterScreenCommunicatorImpl
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.ParachainStakingRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.common.SelectCollatorInterScreenCommunicator
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.parachainStaking.collator.settings.SelectCollatorSettingsInterScreenCommunicator
|
||||
|
||||
@Module
|
||||
class ParachainStakingNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideParachainStakingRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
navigator: Navigator
|
||||
): ParachainStakingRouter {
|
||||
return ParachainStakingNavigator(navigationHoldersRegistry, navigator)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectCollatorCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectCollatorInterScreenCommunicator {
|
||||
return SelectCollatorInterScreenCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideSelectCollatorSettingsCommunicator(navigationHoldersRegistry: NavigationHoldersRegistry): SelectCollatorSettingsInterScreenCommunicator {
|
||||
return SelectCollatorSettingsInterScreenCommunicatorImpl(navigationHoldersRegistry)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation.staking
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.relaychain.RelayStakingNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
|
||||
|
||||
@Module
|
||||
class RelayStakingNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideRelayStakingRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
navigator: Navigator,
|
||||
dashboardRouter: StakingDashboardRouter,
|
||||
dAppRouter: DAppRouter
|
||||
): StakingRouter {
|
||||
return RelayStakingNavigator(navigationHoldersRegistry, navigator, dashboardRouter, dAppRouter)
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.novafoundation.nova.app.di.app.navigation.staking
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.StartMultiStakingNavigator
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingDashboardRouter
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StartMultiStakingRouter
|
||||
|
||||
@Module(
|
||||
includes = [
|
||||
ParachainStakingNavigationModule::class,
|
||||
RelayStakingNavigationModule::class,
|
||||
NominationPoolsStakingNavigationModule::class,
|
||||
MythosStakingNavigationModule::class
|
||||
]
|
||||
)
|
||||
class StakingNavigationModule {
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStakingDashboardNavigator(navigationHoldersRegistry: NavigationHoldersRegistry): StakingDashboardNavigator {
|
||||
return StakingDashboardNavigator(navigationHoldersRegistry)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStakingDashboardRouter(relayStakingNavigator: StakingDashboardNavigator): StakingDashboardRouter = relayStakingNavigator
|
||||
|
||||
@Provides
|
||||
@ApplicationScope
|
||||
fun provideStartMultiStakingRouter(
|
||||
navigationHoldersRegistry: NavigationHoldersRegistry,
|
||||
dashboardRouter: StakingDashboardRouter,
|
||||
commonNavigator: Navigator
|
||||
): StartMultiStakingRouter {
|
||||
return StartMultiStakingNavigator(navigationHoldersRegistry, dashboardRouter, commonNavigator)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
package io.novafoundation.nova.app.di.deps
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
import io.novafoundation.nova.app.App
|
||||
import io.novafoundation.nova.app.root.di.RootApi
|
||||
import io.novafoundation.nova.app.root.di.RootFeatureHolder
|
||||
import io.novafoundation.nova.caip.di.CaipApi
|
||||
import io.novafoundation.nova.caip.di.CaipFeatureHolder
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.core_db.di.DbApi
|
||||
import io.novafoundation.nova.core_db.di.DbHolder
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_account_impl.di.AccountFeatureHolder
|
||||
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureHolder
|
||||
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_ahm_impl.di.ChainMigrationFeatureHolder
|
||||
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
|
||||
import io.novafoundation.nova.feature_assets.di.AssetsFeatureHolder
|
||||
import io.novafoundation.nova.feature_banners_api.di.BannersFeatureApi
|
||||
import io.novafoundation.nova.feature_banners_impl.di.BannersFeatureHolder
|
||||
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
|
||||
import io.novafoundation.nova.feature_buy_impl.di.BuyFeatureHolder
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.di.CloudBackupFeatureApi
|
||||
import io.novafoundation.nova.feature_cloud_backup_impl.di.CloudBackupFeatureHolder
|
||||
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
|
||||
import io.novafoundation.nova.feature_crowdloan_impl.di.CrowdloanFeatureHolder
|
||||
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
||||
import io.novafoundation.nova.feature_currency_impl.di.CurrencyFeatureHolder
|
||||
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
|
||||
import io.novafoundation.nova.feature_dapp_impl.di.DAppFeatureHolder
|
||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureHolder
|
||||
import io.novafoundation.nova.feature_external_sign_api.di.ExternalSignFeatureApi
|
||||
import io.novafoundation.nova.feature_external_sign_impl.di.ExternalSignFeatureHolder
|
||||
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
||||
import io.novafoundation.nova.feature_gift_impl.di.GiftFeatureHolder
|
||||
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_impl.di.GovernanceFeatureHolder
|
||||
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
|
||||
import io.novafoundation.nova.feature_ledger_core.LedgerCoreHolder
|
||||
import io.novafoundation.nova.feature_ledger_core.di.LedgerCoreApi
|
||||
import io.novafoundation.nova.feature_ledger_impl.di.LedgerFeatureHolder
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureHolder
|
||||
import io.novafoundation.nova.feature_nft_api.NftFeatureApi
|
||||
import io.novafoundation.nova.feature_nft_impl.di.NftFeatureHolder
|
||||
import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
|
||||
import io.novafoundation.nova.feature_onboarding_impl.di.OnboardingFeatureHolder
|
||||
import io.novafoundation.nova.feature_proxy_api.di.ProxyFeatureApi
|
||||
import io.novafoundation.nova.feature_proxy_impl.di.ProxyFeatureHolder
|
||||
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
|
||||
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureHolder
|
||||
import io.novafoundation.nova.feature_settings_api.SettingsFeatureApi
|
||||
import io.novafoundation.nova.feature_settings_impl.di.SettingsFeatureHolder
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_impl.di.StakingFeatureHolder
|
||||
import io.novafoundation.nova.feature_swap_api.di.SwapFeatureApi
|
||||
import io.novafoundation.nova.feature_swap_core.di.SwapCoreHolder
|
||||
import io.novafoundation.nova.feature_swap_core_api.di.SwapCoreApi
|
||||
import io.novafoundation.nova.feature_swap_impl.di.SwapFeatureHolder
|
||||
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
|
||||
import io.novafoundation.nova.feature_versions_impl.di.VersionsFeatureHolder
|
||||
import io.novafoundation.nova.feature_vote.di.VoteFeatureApi
|
||||
import io.novafoundation.nova.feature_vote.di.VoteFeatureHolder
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_connect_impl.di.WalletConnectFeatureHolder
|
||||
import io.novafoundation.nova.feature_wallet_impl.di.WalletFeatureHolder
|
||||
import io.novafoundation.nova.feature_xcm_api.di.XcmFeatureApi
|
||||
import io.novafoundation.nova.feature_xcm_impl.di.XcmFeatureHolder
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeHolder
|
||||
import io.novafoundation.nova.splash.di.SplashFeatureApi
|
||||
import io.novafoundation.nova.splash.di.SplashFeatureHolder
|
||||
import io.novafoundation.nova.web3names.di.Web3NamesApi
|
||||
import io.novafoundation.nova.web3names.di.Web3NamesHolder
|
||||
|
||||
@Module
|
||||
interface ComponentHolderModule {
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
fun provideFeatureContainer(application: App): FeatureContainer
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(SplashFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideSplashFeatureHolder(splashFeatureHolder: SplashFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(DbApi::class)
|
||||
@IntoMap
|
||||
fun provideDbFeature(dbHolder: DbHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(OnboardingFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideOnboardingFeature(onboardingFeatureHolder: OnboardingFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(DAppFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideDAppFeature(dAppFeatureHolder: DAppFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(LedgerFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideLedgerFeature(accountFeatureHolder: LedgerFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(LedgerCoreApi::class)
|
||||
@IntoMap
|
||||
fun provideLedgerCore(accountFeatureHolder: LedgerCoreHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(GovernanceFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideGovernanceFeature(accountFeatureHolder: GovernanceFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(AccountFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideAccountFeature(accountFeatureHolder: AccountFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(AssetsFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideAssetsFeature(holder: AssetsFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(VoteFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideVoteFeature(holder: VoteFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(WalletFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideWalletFeature(walletFeatureHolder: WalletFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(CurrencyFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideCurrencyFeature(currencyFeatureHolder: CurrencyFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(RootApi::class)
|
||||
@IntoMap
|
||||
fun provideMainFeature(accountFeatureHolder: RootFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(StakingFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideStakingFeature(holder: StakingFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(RuntimeApi::class)
|
||||
@IntoMap
|
||||
fun provideRuntimeFeature(runtimeHolder: RuntimeHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(Web3NamesApi::class)
|
||||
@IntoMap
|
||||
fun provideWeb3Names(web3NamesHolder: Web3NamesHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(CrowdloanFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideCrowdloanFeature(holder: CrowdloanFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(NftFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideNftFeature(holder: NftFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(VersionsFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideVersionsFeature(holder: VersionsFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(CaipApi::class)
|
||||
@IntoMap
|
||||
fun provideCaipFeature(holder: CaipFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(ExternalSignFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideExternalSignFeature(holder: ExternalSignFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(WalletConnectFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideWalletConnectFeature(holder: WalletConnectFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(SettingsFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideSettingsFeature(holder: SettingsFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(SwapFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideSwapFeature(holder: SwapFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(BuyFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideBuyFeature(holder: BuyFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(PushNotificationsFeatureApi::class)
|
||||
@IntoMap
|
||||
fun providePushNotificationsFeature(holder: PushNotificationsFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(ProxyFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideProxyFeature(holder: ProxyFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(DeepLinkingFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideDeepLinkingFeatureHolder(holder: DeepLinkingFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(CloudBackupFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideCloudBackupFeatureHolder(holder: CloudBackupFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(SwapCoreApi::class)
|
||||
@IntoMap
|
||||
fun provideSwapCoreFeatureHolder(holder: SwapCoreHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(BannersFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideBannersFeatureApi(holder: BannersFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(XcmFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideXcmFeatureHolder(holder: XcmFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(MultisigOperationsFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideMultisigOperationsFeatureHolder(holder: MultisigOperationsFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(AccountMigrationFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideAccountMigrationFeatureHolder(holder: AccountMigrationFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(ChainMigrationFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideChainMigrationFeatureHolder(holder: ChainMigrationFeatureHolder): FeatureApiHolder
|
||||
|
||||
@ApplicationScope
|
||||
@Binds
|
||||
@ClassKey(GiftFeatureApi::class)
|
||||
@IntoMap
|
||||
fun provideGiftFeature(giftFeatureHolder: GiftFeatureHolder): FeatureApiHolder
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.novafoundation.nova.app.di.deps
|
||||
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
|
||||
class FeatureHolderManager(
|
||||
private val mFeatureHolders: Map<Class<*>, FeatureApiHolder>
|
||||
) {
|
||||
|
||||
fun <T> getFeature(key: Class<*>): T? {
|
||||
val featureApiHolder = mFeatureHolders[key] ?: throw IllegalStateException()
|
||||
return featureApiHolder.getFeatureApi<T>()
|
||||
}
|
||||
|
||||
fun releaseFeature(key: Class<*>) {
|
||||
val featureApiHolder = mFeatureHolders[key] ?: throw IllegalStateException()
|
||||
featureApiHolder.releaseFeatureApi()
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoSet
|
||||
import io.novafoundation.nova.app.root.presentation.common.FirebaseServiceInitializer
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.interfaces.CompoundExternalServiceInitializer
|
||||
import io.novafoundation.nova.common.interfaces.ExternalServiceInitializer
|
||||
|
||||
@Module
|
||||
class ExternalServiceInitializersModule {
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideFirebaseServiceInitializer(
|
||||
context: Context
|
||||
): ExternalServiceInitializer {
|
||||
return FirebaseServiceInitializer(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideCompoundExternalServiceInitializer(
|
||||
initializers: Set<@JvmSuppressWildcards ExternalServiceInitializer>
|
||||
): ExternalServiceInitializer {
|
||||
return CompoundExternalServiceInitializer(initializers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
interface RootApi
|
||||
@@ -0,0 +1,103 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
|
||||
import io.novafoundation.nova.app.root.presentation.RootRouter
|
||||
import io.novafoundation.nova.app.root.presentation.di.RootActivityComponent
|
||||
import io.novafoundation.nova.app.root.presentation.main.di.MainFragmentComponent
|
||||
import io.novafoundation.nova.app.root.presentation.splitScreen.di.SplitScreenFragmentComponent
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
|
||||
import io.novafoundation.nova.core_db.di.DbApi
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
|
||||
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
|
||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
|
||||
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
|
||||
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
||||
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
|
||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
||||
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
|
||||
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
|
||||
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
|
||||
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
RootDependencies::class
|
||||
],
|
||||
modules = [
|
||||
RootFeatureModule::class
|
||||
]
|
||||
)
|
||||
@FeatureScope
|
||||
interface RootComponent {
|
||||
|
||||
fun mainActivityComponentFactory(): RootActivityComponent.Factory
|
||||
|
||||
fun splitScreenFragmentComponentFactory(): SplitScreenFragmentComponent.Factory
|
||||
|
||||
fun mainFragmentComponentFactory(): MainFragmentComponent.Factory
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
|
||||
fun create(
|
||||
@BindsInstance splitScreenNavigationHolder: SplitScreenNavigationHolder,
|
||||
@BindsInstance rootNavigationHolder: RootNavigationHolder,
|
||||
@BindsInstance rootRouter: RootRouter,
|
||||
@BindsInstance governanceRouter: GovernanceRouter,
|
||||
@BindsInstance dAppRouter: DAppRouter,
|
||||
@BindsInstance assetsRouter: AssetsRouter,
|
||||
@BindsInstance accountRouter: AccountRouter,
|
||||
@BindsInstance stakingRouter: StakingRouter,
|
||||
@BindsInstance stakingDashboardNavigator: StakingDashboardNavigator,
|
||||
@BindsInstance delayedNavigationRouter: DelayedNavigationRouter,
|
||||
deps: RootDependencies
|
||||
): RootComponent
|
||||
}
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
AccountFeatureApi::class,
|
||||
WalletFeatureApi::class,
|
||||
StakingFeatureApi::class,
|
||||
CrowdloanFeatureApi::class,
|
||||
AssetsFeatureApi::class,
|
||||
CurrencyFeatureApi::class,
|
||||
GovernanceFeatureApi::class,
|
||||
DAppFeatureApi::class,
|
||||
DbApi::class,
|
||||
CommonApi::class,
|
||||
RuntimeApi::class,
|
||||
VersionsFeatureApi::class,
|
||||
WalletConnectFeatureApi::class,
|
||||
PushNotificationsFeatureApi::class,
|
||||
DeepLinkingFeatureApi::class,
|
||||
LedgerFeatureApi::class,
|
||||
BuyFeatureApi::class,
|
||||
DeepLinkingFeatureApi::class,
|
||||
AccountMigrationFeatureApi::class,
|
||||
MultisigOperationsFeatureApi::class,
|
||||
ChainMigrationFeatureApi::class,
|
||||
GiftFeatureApi::class
|
||||
]
|
||||
)
|
||||
interface RootFeatureDependenciesComponent : RootDependencies
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
import android.content.Context
|
||||
import coil.ImageLoader
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
|
||||
import io.novafoundation.nova.common.mixin.api.NetworkStateMixin
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.sequrity.SafeModeService
|
||||
import io.novafoundation.nova.common.utils.DialogMessageManager
|
||||
import io.novafoundation.nova.common.utils.ToastMessageManager
|
||||
import io.novafoundation.nova.common.utils.coroutines.RootScope
|
||||
import io.novafoundation.nova.common.utils.network.DeviceNetworkStateObserver
|
||||
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
|
||||
import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
|
||||
import io.novafoundation.nova.common.utils.systemCall.SystemCallExecutor
|
||||
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncher
|
||||
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncherFactory
|
||||
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
|
||||
import io.novafoundation.nova.feature_account_api.data.events.MetaAccountChangesEventBus
|
||||
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
|
||||
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
|
||||
import io.novafoundation.nova.feature_account_api.data.multisig.validation.MultisigExtrinsicValidationRequestBus
|
||||
import io.novafoundation.nova.feature_account_api.data.proxy.validation.ProxyExtrinsicValidationRequestBus
|
||||
import io.novafoundation.nova.feature_account_api.di.deeplinks.AccountDeepLinks
|
||||
import io.novafoundation.nova.feature_account_api.domain.account.common.EncryptionDefaults
|
||||
import io.novafoundation.nova.feature_account_api.domain.cloudBackup.ApplyLocalSnapshotToCloudBackupUseCase
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_account_migration.di.deeplinks.AccountMigrationDeepLinks
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.di.deeplinks.ChainMigrationDeepLinks
|
||||
import io.novafoundation.nova.feature_ahm_api.domain.ChainMigrationDetailsSelectToShowUseCase
|
||||
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
|
||||
import io.novafoundation.nova.feature_assets.di.modules.deeplinks.AssetDeepLinks
|
||||
import io.novafoundation.nova.feature_buy_api.di.deeplinks.BuyDeepLinks
|
||||
import io.novafoundation.nova.feature_crowdloan_api.data.repository.CrowdloanRepository
|
||||
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
|
||||
import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
|
||||
import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserTabExternalRepository
|
||||
import io.novafoundation.nova.feature_dapp_api.data.repository.DAppMetadataRepository
|
||||
import io.novafoundation.nova.feature_dapp_api.di.deeplinks.DAppDeepLinks
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.PendingDeepLinkProvider
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIoLinkConverter
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.common.DeepLinkingPreferences
|
||||
import io.novafoundation.nova.feature_gift_api.di.GiftDeepLinks
|
||||
import io.novafoundation.nova.feature_governance_api.data.MutableGovernanceState
|
||||
import io.novafoundation.nova.feature_governance_api.di.deeplinks.GovernanceDeepLinks
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.deeplink.MultisigDeepLinks
|
||||
import io.novafoundation.nova.feature_push_notifications.domain.interactor.PushNotificationsInteractor
|
||||
import io.novafoundation.nova.feature_push_notifications.domain.interactor.WelcomePushNotificationsInteractor
|
||||
import io.novafoundation.nova.feature_push_notifications.presentation.multisigsWarning.MultisigPushNotificationsAlertMixinFactory
|
||||
import io.novafoundation.nova.feature_staking_api.di.deeplinks.StakingDeepLinks
|
||||
import io.novafoundation.nova.feature_staking_api.domain.api.StakingRepository
|
||||
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.validation.MultisigExtrinsicValidationFactory
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.validation.ProxyHaveEnoughFeeValidationFactory
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.di.deeplinks.WalletConnectDeepLinks
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.domain.sessions.WalletConnectSessionsUseCase
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.presentation.WalletConnectService
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
interface RootDependencies {
|
||||
|
||||
val stakingDeepLinks: StakingDeepLinks
|
||||
|
||||
val accountDeepLinks: AccountDeepLinks
|
||||
|
||||
val dAppDeepLinks: DAppDeepLinks
|
||||
|
||||
val governanceDeepLinks: GovernanceDeepLinks
|
||||
|
||||
val buyDeepLinks: BuyDeepLinks
|
||||
|
||||
val assetDeepLinks: AssetDeepLinks
|
||||
|
||||
val giftDeepLinks: GiftDeepLinks
|
||||
|
||||
val chainMigrationDeepLinks: ChainMigrationDeepLinks
|
||||
|
||||
val walletConnectDeepLinks: WalletConnectDeepLinks
|
||||
|
||||
val systemCallExecutor: SystemCallExecutor
|
||||
|
||||
val contextManager: ContextManager
|
||||
|
||||
val walletConnectService: WalletConnectService
|
||||
|
||||
val imageLoader: ImageLoader
|
||||
|
||||
val automaticInteractionGate: AutomaticInteractionGate
|
||||
|
||||
val walletConnectSessionsUseCase: WalletConnectSessionsUseCase
|
||||
|
||||
val pushNotificationsInteractor: PushNotificationsInteractor
|
||||
|
||||
val welcomePushNotificationsInteractor: WelcomePushNotificationsInteractor
|
||||
|
||||
val applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase
|
||||
|
||||
val actionBottomSheetLauncherFactory: ActionBottomSheetLauncherFactory
|
||||
|
||||
val tabsDao: BrowserTabsDao
|
||||
|
||||
val balancesUpdateSystem: BalancesUpdateSystem
|
||||
|
||||
val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
|
||||
|
||||
val browserTabExternalRepository: BrowserTabExternalRepository
|
||||
|
||||
val externalAccountsSyncService: ExternalAccountsSyncService
|
||||
|
||||
val multisigPendingOperationsService: MultisigPendingOperationsService
|
||||
|
||||
val accountMigrationDeepLinks: AccountMigrationDeepLinks
|
||||
|
||||
val multisigDeepLinks: MultisigDeepLinks
|
||||
|
||||
val deepLinkingPreferences: DeepLinkingPreferences
|
||||
|
||||
val branchIoLinkConverter: BranchIoLinkConverter
|
||||
|
||||
val pendingDeepLinkProvider: PendingDeepLinkProvider
|
||||
|
||||
val multisigExtrinsicValidationRequestBus: MultisigExtrinsicValidationRequestBus
|
||||
|
||||
val multisigExtrinsicValidationFactory: MultisigExtrinsicValidationFactory
|
||||
|
||||
val actionBottomSheetLauncher: ActionBottomSheetLauncher
|
||||
|
||||
val multisigPushNotificationsAlertMixinFactory: MultisigPushNotificationsAlertMixinFactory
|
||||
|
||||
val chainMigrationDetailsSelectToShowUseCase: ChainMigrationDetailsSelectToShowUseCase
|
||||
|
||||
val deviceNetworkStateObserver: DeviceNetworkStateObserver
|
||||
|
||||
fun updateNotificationsInteractor(): UpdateNotificationsInteractor
|
||||
|
||||
fun contributionsInteractor(): ContributionsInteractor
|
||||
|
||||
fun crowdloanRepository(): CrowdloanRepository
|
||||
|
||||
fun networkStateMixin(): NetworkStateMixin
|
||||
|
||||
fun externalRequirementsFlow(): MutableStateFlow<ChainConnection.ExternalRequirement>
|
||||
|
||||
fun accountRepository(): AccountRepository
|
||||
|
||||
fun walletRepository(): WalletRepository
|
||||
|
||||
fun appLinksProvider(): AppLinksProvider
|
||||
|
||||
fun resourceManager(): ResourceManager
|
||||
|
||||
fun currencyInteractor(): CurrencyInteractor
|
||||
|
||||
fun stakingRepository(): StakingRepository
|
||||
|
||||
fun chainRegistry(): ChainRegistry
|
||||
|
||||
fun backgroundAccessObserver(): BackgroundAccessObserver
|
||||
|
||||
fun safeModeService(): SafeModeService
|
||||
|
||||
fun rootScope(): RootScope
|
||||
|
||||
fun governanceStateUpdater(): MutableGovernanceState
|
||||
|
||||
fun dappMetadataRepository(): DAppMetadataRepository
|
||||
|
||||
fun encryptionDefaults(): EncryptionDefaults
|
||||
|
||||
fun proxyExtrinsicValidationRequestBus(): ProxyExtrinsicValidationRequestBus
|
||||
|
||||
fun metaAccountChangesRequestBus(): MetaAccountChangesEventBus
|
||||
|
||||
fun proxyHaveEnoughFeeValidationFactory(): ProxyHaveEnoughFeeValidationFactory
|
||||
|
||||
fun context(): Context
|
||||
|
||||
fun toastMessageManager(): ToastMessageManager
|
||||
|
||||
fun dialogMessageManager(): DialogMessageManager
|
||||
|
||||
fun chainMigrationRepository(): ChainMigrationRepository
|
||||
|
||||
fun migrationInfoRepository(): MigrationInfoRepository
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
import io.novafoundation.nova.app.root.navigation.holders.RootNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.holders.SplitScreenNavigationHolder
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.Navigator
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.staking.StakingDashboardNavigator
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.common.navigation.DelayedNavigationRouter
|
||||
import io.novafoundation.nova.core_db.di.DbApi
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
|
||||
import io.novafoundation.nova.feature_account_migration.di.AccountMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_ahm_api.di.ChainMigrationFeatureApi
|
||||
import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
|
||||
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
|
||||
import io.novafoundation.nova.feature_buy_api.di.BuyFeatureApi
|
||||
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
|
||||
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
|
||||
import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi
|
||||
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
|
||||
import io.novafoundation.nova.feature_deep_linking.di.DeepLinkingFeatureApi
|
||||
import io.novafoundation.nova.feature_gift_api.di.GiftFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_api.di.GovernanceFeatureApi
|
||||
import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter
|
||||
import io.novafoundation.nova.feature_ledger_api.di.LedgerFeatureApi
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.MultisigOperationsFeatureApi
|
||||
import io.novafoundation.nova.feature_push_notifications.di.PushNotificationsFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
|
||||
import io.novafoundation.nova.feature_staking_impl.presentation.StakingRouter
|
||||
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.di.WalletConnectFeatureApi
|
||||
import io.novafoundation.nova.runtime.di.RuntimeApi
|
||||
import javax.inject.Inject
|
||||
|
||||
@ApplicationScope
|
||||
class RootFeatureHolder @Inject constructor(
|
||||
private val splitScreenNavigationHolder: SplitScreenNavigationHolder,
|
||||
private val rootNavigationHolder: RootNavigationHolder,
|
||||
private val navigator: Navigator,
|
||||
private val governanceRouter: GovernanceRouter,
|
||||
private val dAppRouter: DAppRouter,
|
||||
private val accountRouter: AccountRouter,
|
||||
private val assetsRouter: AssetsRouter,
|
||||
private val stakingRouter: StakingRouter,
|
||||
private val stakingDashboardNavigator: StakingDashboardNavigator,
|
||||
private val delayedNavRouter: DelayedNavigationRouter,
|
||||
featureContainer: FeatureContainer
|
||||
) : FeatureApiHolder(featureContainer) {
|
||||
|
||||
override fun initializeDependencies(): Any {
|
||||
val rootFeatureDependencies = DaggerRootComponent_RootFeatureDependenciesComponent.builder()
|
||||
.commonApi(commonApi())
|
||||
.dbApi(getFeature(DbApi::class.java))
|
||||
.accountFeatureApi(getFeature(AccountFeatureApi::class.java))
|
||||
.walletFeatureApi(getFeature(WalletFeatureApi::class.java))
|
||||
.stakingFeatureApi(getFeature(StakingFeatureApi::class.java))
|
||||
.assetsFeatureApi(getFeature(AssetsFeatureApi::class.java))
|
||||
.currencyFeatureApi(getFeature(CurrencyFeatureApi::class.java))
|
||||
.crowdloanFeatureApi(getFeature(CrowdloanFeatureApi::class.java))
|
||||
.governanceFeatureApi(getFeature(GovernanceFeatureApi::class.java))
|
||||
.dAppFeatureApi(getFeature(DAppFeatureApi::class.java))
|
||||
.runtimeApi(getFeature(RuntimeApi::class.java))
|
||||
.versionsFeatureApi(getFeature(VersionsFeatureApi::class.java))
|
||||
.walletConnectFeatureApi(getFeature(WalletConnectFeatureApi::class.java))
|
||||
.pushNotificationsFeatureApi(getFeature(PushNotificationsFeatureApi::class.java))
|
||||
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
|
||||
.ledgerFeatureApi(getFeature(LedgerFeatureApi::class.java))
|
||||
.buyFeatureApi(getFeature(BuyFeatureApi::class.java))
|
||||
.deepLinkingFeatureApi(getFeature(DeepLinkingFeatureApi::class.java))
|
||||
.accountMigrationFeatureApi(getFeature(AccountMigrationFeatureApi::class.java))
|
||||
.multisigOperationsFeatureApi(getFeature(MultisigOperationsFeatureApi::class.java))
|
||||
.chainMigrationFeatureApi(getFeature(ChainMigrationFeatureApi::class.java))
|
||||
.giftFeatureApi(getFeature(GiftFeatureApi::class.java))
|
||||
.build()
|
||||
|
||||
return DaggerRootComponent.factory()
|
||||
.create(
|
||||
splitScreenNavigationHolder,
|
||||
rootNavigationHolder,
|
||||
navigator,
|
||||
governanceRouter,
|
||||
dAppRouter,
|
||||
assetsRouter,
|
||||
accountRouter,
|
||||
stakingRouter,
|
||||
stakingDashboardNavigator,
|
||||
delayedNavRouter,
|
||||
rootFeatureDependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.novafoundation.nova.app.root.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.app.root.di.busHandler.RequestBusHandlerModule
|
||||
import io.novafoundation.nova.app.root.di.deeplink.DeepLinksModule
|
||||
import io.novafoundation.nova.app.root.domain.RootInteractor
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
|
||||
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
|
||||
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
|
||||
|
||||
@Module(
|
||||
includes = [
|
||||
RequestBusHandlerModule::class,
|
||||
ExternalServiceInitializersModule::class,
|
||||
DeepLinksModule::class
|
||||
]
|
||||
)
|
||||
class RootFeatureModule {
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideRootInteractor(
|
||||
walletRepository: WalletRepository,
|
||||
accountRepository: AccountRepository,
|
||||
balancesUpdateSystem: BalancesUpdateSystem,
|
||||
multisigPendingOperationsService: MultisigPendingOperationsService,
|
||||
externalAccountsSyncService: ExternalAccountsSyncService,
|
||||
chainMigrationRepository: ChainMigrationRepository,
|
||||
migrationInfoRepository: MigrationInfoRepository
|
||||
): RootInteractor {
|
||||
return RootInteractor(
|
||||
updateSystem = balancesUpdateSystem,
|
||||
walletRepository = walletRepository,
|
||||
accountRepository = accountRepository,
|
||||
multisigPendingOperationsService = multisigPendingOperationsService,
|
||||
externalAccountsSyncService = externalAccountsSyncService,
|
||||
chainMigrationRepository = chainMigrationRepository,
|
||||
migrationInfoRepository = migrationInfoRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package io.novafoundation.nova.app.root.di.busHandler
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoSet
|
||||
import io.novafoundation.nova.app.root.presentation.RootRouter
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.CloudBackupSyncRequestBusHandler
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.CompoundRequestBusHandler
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.MultisigExtrinsicValidationRequestBusHandler
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.ProxyExtrinsicValidationRequestBusHandler
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.PushSettingsSyncRequestBusHandler
|
||||
import io.novafoundation.nova.app.root.presentation.requestBusHandler.RequestBusHandler
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
|
||||
import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncher
|
||||
import io.novafoundation.nova.feature_account_api.data.proxy.validation.ProxyExtrinsicValidationRequestBus
|
||||
import io.novafoundation.nova.feature_account_api.data.events.MetaAccountChangesEventBus
|
||||
import io.novafoundation.nova.feature_account_api.data.multisig.validation.MultisigExtrinsicValidationRequestBus
|
||||
import io.novafoundation.nova.feature_account_api.domain.cloudBackup.ApplyLocalSnapshotToCloudBackupUseCase
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_push_notifications.domain.interactor.PushNotificationsInteractor
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.validation.MultisigExtrinsicValidationFactory
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.validation.ProxyHaveEnoughFeeValidationFactory
|
||||
|
||||
@Module
|
||||
class RequestBusHandlerModule {
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
@IntoSet
|
||||
fun providePushSettingsSyncRequestBusHandler(
|
||||
metaAccountChangesEventBus: MetaAccountChangesEventBus,
|
||||
pushNotificationsInteractor: PushNotificationsInteractor
|
||||
): RequestBusHandler {
|
||||
return PushSettingsSyncRequestBusHandler(
|
||||
metaAccountChangesEventBus,
|
||||
pushNotificationsInteractor
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
@IntoSet
|
||||
fun provideProxyExtrinsicValidationRequestBusHandler(
|
||||
proxyProxyExtrinsicValidationRequestBus: ProxyExtrinsicValidationRequestBus,
|
||||
proxyHaveEnoughFeeValidationFactory: ProxyHaveEnoughFeeValidationFactory
|
||||
): RequestBusHandler {
|
||||
return ProxyExtrinsicValidationRequestBusHandler(
|
||||
proxyProxyExtrinsicValidationRequestBus,
|
||||
proxyHaveEnoughFeeValidationFactory
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
@IntoSet
|
||||
fun provideMultisigExtrinsicValidationRequestBusHandler(
|
||||
multisigExtrinsicValidationRequestBus: MultisigExtrinsicValidationRequestBus,
|
||||
multisigExtrinsicValidationFactory: MultisigExtrinsicValidationFactory
|
||||
): RequestBusHandler {
|
||||
return MultisigExtrinsicValidationRequestBusHandler(
|
||||
multisigExtrinsicValidationRequestBus,
|
||||
multisigExtrinsicValidationFactory
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
@IntoSet
|
||||
fun provideCloudBackupSyncRequestBusHandler(
|
||||
rootRouter: RootRouter,
|
||||
resourceManager: ResourceManager,
|
||||
metaAccountChangesEventBus: MetaAccountChangesEventBus,
|
||||
applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase,
|
||||
accountRepository: AccountRepository,
|
||||
actionBottomSheetLauncher: ActionBottomSheetLauncher,
|
||||
automaticInteractionGate: AutomaticInteractionGate
|
||||
): RequestBusHandler {
|
||||
return CloudBackupSyncRequestBusHandler(
|
||||
rootRouter,
|
||||
resourceManager,
|
||||
metaAccountChangesEventBus,
|
||||
applyLocalSnapshotToCloudBackupUseCase,
|
||||
accountRepository,
|
||||
actionBottomSheetLauncher,
|
||||
automaticInteractionGate
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideCompoundRequestBusHandler(
|
||||
handlers: Set<@JvmSuppressWildcards RequestBusHandler>
|
||||
): CompoundRequestBusHandler {
|
||||
return CompoundRequestBusHandler(handlers)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package io.novafoundation.nova.app.root.di.deeplink
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.feature_account_api.di.deeplinks.AccountDeepLinks
|
||||
import io.novafoundation.nova.feature_account_migration.di.deeplinks.AccountMigrationDeepLinks
|
||||
import io.novafoundation.nova.feature_ahm_api.di.deeplinks.ChainMigrationDeepLinks
|
||||
import io.novafoundation.nova.feature_assets.di.modules.deeplinks.AssetDeepLinks
|
||||
import io.novafoundation.nova.feature_buy_api.di.deeplinks.BuyDeepLinks
|
||||
import io.novafoundation.nova.feature_dapp_api.di.deeplinks.DAppDeepLinks
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.DeepLinkHandler
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.PendingDeepLinkProvider
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.RootDeepLinkHandler
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIOLinkHandler
|
||||
import io.novafoundation.nova.feature_deep_linking.presentation.handling.branchIo.BranchIoLinkConverter
|
||||
import io.novafoundation.nova.feature_gift_api.di.GiftDeepLinks
|
||||
import io.novafoundation.nova.feature_governance_api.di.deeplinks.GovernanceDeepLinks
|
||||
import io.novafoundation.nova.feature_multisig_operations.di.deeplink.MultisigDeepLinks
|
||||
import io.novafoundation.nova.feature_staking_api.di.deeplinks.StakingDeepLinks
|
||||
import io.novafoundation.nova.feature_wallet_connect_api.di.deeplinks.WalletConnectDeepLinks
|
||||
|
||||
@Module
|
||||
class DeepLinksModule {
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideDeepLinkHandlers(
|
||||
stakingDeepLinks: StakingDeepLinks,
|
||||
accountDeepLinks: AccountDeepLinks,
|
||||
dAppDeepLinks: DAppDeepLinks,
|
||||
governanceDeepLinks: GovernanceDeepLinks,
|
||||
buyDeepLinks: BuyDeepLinks,
|
||||
assetDeepLinks: AssetDeepLinks,
|
||||
walletConnectDeepLinks: WalletConnectDeepLinks,
|
||||
accountMigrationDeepLinks: AccountMigrationDeepLinks,
|
||||
multisigDeepLinks: MultisigDeepLinks,
|
||||
chainMigrationDeepLinks: ChainMigrationDeepLinks,
|
||||
giftDeepLinks: GiftDeepLinks
|
||||
): List<@JvmWildcard DeepLinkHandler> {
|
||||
return buildList {
|
||||
addAll(stakingDeepLinks.deepLinkHandlers)
|
||||
addAll(accountDeepLinks.deepLinkHandlers)
|
||||
addAll(dAppDeepLinks.deepLinkHandlers)
|
||||
addAll(governanceDeepLinks.deepLinkHandlers)
|
||||
addAll(buyDeepLinks.deepLinkHandlers)
|
||||
addAll(assetDeepLinks.deepLinkHandlers)
|
||||
addAll(walletConnectDeepLinks.deepLinkHandlers)
|
||||
addAll(accountMigrationDeepLinks.deepLinkHandlers)
|
||||
addAll(multisigDeepLinks.deepLinkHandlers)
|
||||
addAll(chainMigrationDeepLinks.deepLinkHandlers)
|
||||
addAll(giftDeepLinks.deepLinkHandlers)
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideRootDeepLinkHandler(
|
||||
pendingDeepLinkProvider: PendingDeepLinkProvider,
|
||||
nestedHandlers: @JvmWildcard List<DeepLinkHandler>
|
||||
): RootDeepLinkHandler {
|
||||
return RootDeepLinkHandler(
|
||||
pendingDeepLinkProvider,
|
||||
nestedHandlers
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FeatureScope
|
||||
fun provideBranchIOLinkHandler(
|
||||
branchIoLinkConverter: BranchIoLinkConverter
|
||||
): BranchIOLinkHandler {
|
||||
return BranchIOLinkHandler(branchIoLinkConverter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.novafoundation.nova.app.root.domain
|
||||
|
||||
import io.novafoundation.nova.common.data.memory.ComputationalScope
|
||||
import io.novafoundation.nova.core.updater.Updater
|
||||
import io.novafoundation.nova.feature_account_api.data.externalAccounts.ExternalAccountsSyncService
|
||||
import io.novafoundation.nova.feature_account_api.data.multisig.MultisigPendingOperationsService
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.ChainMigrationRepository
|
||||
import io.novafoundation.nova.feature_ahm_api.data.repository.MigrationInfoRepository
|
||||
import io.novafoundation.nova.feature_assets.data.network.BalancesUpdateSystem
|
||||
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class RootInteractor(
|
||||
private val updateSystem: BalancesUpdateSystem,
|
||||
private val walletRepository: WalletRepository,
|
||||
private val accountRepository: AccountRepository,
|
||||
private val multisigPendingOperationsService: MultisigPendingOperationsService,
|
||||
private val externalAccountsSyncService: ExternalAccountsSyncService,
|
||||
private val chainMigrationRepository: ChainMigrationRepository,
|
||||
private val migrationInfoRepository: MigrationInfoRepository
|
||||
) {
|
||||
|
||||
fun runBalancesUpdate(): Flow<Updater.SideEffect> = updateSystem.start()
|
||||
|
||||
suspend fun updatePhishingAddresses() {
|
||||
runCatching {
|
||||
walletRepository.updatePhishingAddresses()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun isAccountSelected(): Boolean {
|
||||
return accountRepository.isAccountSelected()
|
||||
}
|
||||
|
||||
suspend fun isPinCodeSet(): Boolean {
|
||||
return accountRepository.isCodeSet()
|
||||
}
|
||||
|
||||
fun syncExternalAccounts() {
|
||||
externalAccountsSyncService.sync()
|
||||
}
|
||||
|
||||
context(ComputationalScope)
|
||||
fun syncPendingMultisigOperations(): Flow<Unit> {
|
||||
return multisigPendingOperationsService.performMultisigOperationsSync()
|
||||
}
|
||||
|
||||
suspend fun cacheBalancesForChainMigrationDetection() {
|
||||
chainMigrationRepository.cacheBalancesForChainMigrationDetection()
|
||||
}
|
||||
|
||||
suspend fun loadMigrationDetailsConfigs() {
|
||||
migrationInfoRepository.loadConfigs()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.novafoundation.nova.app.root.domain
|
||||
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_dapp_api.data.model.SimpleTabModel
|
||||
import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserTabExternalRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
||||
class SplitScreenInteractor(
|
||||
private val repository: BrowserTabExternalRepository,
|
||||
private val accountRepository: AccountRepository
|
||||
) {
|
||||
|
||||
fun observeTabNamesById(): Flow<List<SimpleTabModel>> {
|
||||
return accountRepository.selectedMetaAccountFlow()
|
||||
.flatMapLatest { repository.observeTabsWithNames(it.id) }
|
||||
}
|
||||
|
||||
suspend fun removeAllTabs() {
|
||||
val metaAccount = accountRepository.getSelectedMetaAccount()
|
||||
repository.removeTabsForMetaAccount(metaAccount.id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.novafoundation.nova.app.root.navigation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraph
|
||||
import io.novafoundation.nova.app.R
|
||||
import io.novafoundation.nova.app.root.navigation.delayedNavigation.NavComponentDelayedNavigation
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator
|
||||
import io.novafoundation.nova.app.root.presentation.splitScreen.SplitScreenFragment
|
||||
import io.novafoundation.nova.app.root.presentation.splitScreen.SplitScreenPayload
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun NavController.getBackStackEntryBefore(@IdRes id: Int): NavBackStackEntry {
|
||||
val initial = getBackStackEntry(id)
|
||||
val backStack = backStack.toList()
|
||||
|
||||
val initialIndex = backStack.indexOf(initial)
|
||||
|
||||
var previousIndex = initialIndex - 1
|
||||
|
||||
// ignore nav graphs
|
||||
while (previousIndex > 0 && backStack[previousIndex].destination is NavGraph) {
|
||||
previousIndex--
|
||||
}
|
||||
|
||||
return backStack[previousIndex]
|
||||
}
|
||||
|
||||
fun BaseNavigator.openSplitScreenWithInstantAction(actionId: Int, nestedActionExtras: Bundle? = null) {
|
||||
val delayedNavigation = NavComponentDelayedNavigation(actionId, nestedActionExtras)
|
||||
|
||||
val splitScreenPayload = SplitScreenPayload.InstantNavigationOnAttach(delayedNavigation)
|
||||
navigationBuilder().action(R.id.action_open_split_screen)
|
||||
.setArgs(SplitScreenFragment.createPayload(splitScreenPayload))
|
||||
.navigateInRoot()
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.app.root.navigation
|
||||
|
||||
import io.novafoundation.nova.common.navigation.InterScreenCommunicator
|
||||
import io.novafoundation.nova.common.utils.singleReplaySharedFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class FlowInterScreenCommunicator<I : Any, O : Any> :
|
||||
InterScreenCommunicator<I, O>,
|
||||
CoroutineScope by CoroutineScope(Dispatchers.Main) {
|
||||
|
||||
private var response: O? = null
|
||||
|
||||
override val responseFlow = singleReplaySharedFlow<O>()
|
||||
|
||||
private var _request: I? = null
|
||||
|
||||
override val latestResponse: O?
|
||||
get() = response
|
||||
|
||||
override val lastState: O?
|
||||
get() = latestResponse
|
||||
|
||||
override val lastInput: I?
|
||||
get() = _request
|
||||
|
||||
abstract fun dispatchRequest(request: I)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun openRequest(request: I) {
|
||||
_request = request
|
||||
response = null
|
||||
responseFlow.resetReplayCache()
|
||||
|
||||
dispatchRequest(request)
|
||||
}
|
||||
|
||||
override fun respond(response: O) {
|
||||
launch {
|
||||
this@FlowInterScreenCommunicator.response = response
|
||||
responseFlow.emit(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package io.novafoundation.nova.app.root.navigation
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.NavigationHoldersRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.builder.NavigationBuilderRegistry
|
||||
import io.novafoundation.nova.app.root.navigation.navigators.navigationBuilder
|
||||
import io.novafoundation.nova.common.navigation.InterScreenCommunicator
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.UUID
|
||||
|
||||
abstract class NavStackInterScreenCommunicator<I : Parcelable, O : Parcelable>(
|
||||
private val navigationHoldersRegistry: NavigationHoldersRegistry
|
||||
) : InterScreenCommunicator<I, O> {
|
||||
|
||||
private val responseKey = UUID.randomUUID().toString()
|
||||
private val requestKey = UUID.randomUUID().toString()
|
||||
|
||||
protected val navController: NavController
|
||||
get() = navigationHoldersRegistry.firstAttachedNavController!!
|
||||
|
||||
// from requester - retrieve from current entry
|
||||
override val latestResponse: O?
|
||||
get() = navController.currentBackStackEntry!!.savedStateHandle
|
||||
.get(responseKey)
|
||||
|
||||
// from responder - retrieve from previous (requester) entry
|
||||
override val lastState: O?
|
||||
get() = navController.previousBackStackEntry!!.savedStateHandle
|
||||
.get(responseKey)
|
||||
|
||||
override val responseFlow: Flow<O>
|
||||
get() = createResponseFlow()
|
||||
|
||||
// from responder - retrieve from previous (requester) entry
|
||||
override val lastInput: I?
|
||||
get() = navController.previousBackStackEntry!!.savedStateHandle
|
||||
.get(requestKey)
|
||||
|
||||
@CallSuper
|
||||
override fun openRequest(request: I) {
|
||||
saveRequest(request)
|
||||
}
|
||||
|
||||
fun clearedResponseFlow(): Flow<O> {
|
||||
navController.currentBackStackEntry!!.savedStateHandle.apply {
|
||||
remove<O>(requestKey)
|
||||
remove<O>(responseKey)
|
||||
}
|
||||
return createResponseFlow()
|
||||
}
|
||||
|
||||
override fun respond(response: O) {
|
||||
// previousBackStackEntry since we want to report to previous screen
|
||||
saveResultTo(navController.previousBackStackEntry!!, response)
|
||||
}
|
||||
|
||||
protected fun saveResultTo(backStackEntry: NavBackStackEntry, response: O) {
|
||||
backStackEntry.savedStateHandle.set(responseKey, response)
|
||||
}
|
||||
|
||||
private fun saveRequest(request: I) {
|
||||
navController.currentBackStackEntry!!.savedStateHandle.set(requestKey, request)
|
||||
}
|
||||
|
||||
private fun createResponseFlow(): Flow<O> {
|
||||
return navController.currentBackStackEntry!!.savedStateHandle
|
||||
.getLiveData<O>(responseKey)
|
||||
.asFlow()
|
||||
}
|
||||
|
||||
protected fun navigationBuilder(): NavigationBuilderRegistry {
|
||||
return navigationHoldersRegistry.navigationBuilder()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package io.novafoundation.nova.app.root.navigation.delayedNavigation
|
||||
|
||||
import io.novafoundation.nova.common.navigation.DelayedNavigation
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
object BackDelayedNavigation : DelayedNavigation
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package io.novafoundation.nova.app.root.navigation.delayedNavigation
|
||||
|
||||
import android.os.Bundle
|
||||
import io.novafoundation.nova.common.navigation.DelayedNavigation
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class NavComponentDelayedNavigation(val globalActionId: Int, val extras: Bundle? = null) : DelayedNavigation
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.app.root.navigation.holders
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.novafoundation.nova.common.resources.ContextManager
|
||||
|
||||
abstract class NavigationHolder(val contextManager: ContextManager) {
|
||||
|
||||
var navController: NavController? = null
|
||||
private set
|
||||
|
||||
fun isControllerAttached(): Boolean {
|
||||
return navController != null
|
||||
}
|
||||
|
||||
fun attach(navController: NavController) {
|
||||
this.navController = navController
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the current navController only if it matches the one provided.
|
||||
* This check ensures that if a new screen with a navController is attached,
|
||||
* it doesn't lose its navController when the previous screen calls detach.
|
||||
* By verifying equality, we prevent unintended detachment.
|
||||
*/
|
||||
fun detachNavController(navController: NavController) {
|
||||
if (this.navController == navController) {
|
||||
this.navController = null
|
||||
}
|
||||
}
|
||||
|
||||
fun detach() {
|
||||
navController = null
|
||||
}
|
||||
|
||||
fun finishApp() {
|
||||
contextManager.getActivity()?.finish()
|
||||
}
|
||||
|
||||
fun executeBack() {
|
||||
val popped = navController!!.popBackStack()
|
||||
|
||||
if (!popped) {
|
||||
contextManager.getActivity()!!.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user