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