From 218cf40cd6dbc5f388107f474f58671e31dc48c8 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Mon, 19 Jan 2026 18:15:57 +0100 Subject: [PATCH 1/5] migration wallet setup task --- .github/workflows/migration-wallet-setup.yml | 134 +++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .github/workflows/migration-wallet-setup.yml diff --git a/.github/workflows/migration-wallet-setup.yml b/.github/workflows/migration-wallet-setup.yml new file mode 100644 index 0000000..5ca042b --- /dev/null +++ b/.github/workflows/migration-wallet-setup.yml @@ -0,0 +1,134 @@ +name: Migration Wallet Setup + +on: + workflow_call: + inputs: + e2e_branch: + description: "Branch of synonymdev/bitkit-e2e-tests to use" + required: true + type: string + rn_version: + description: "Legacy RN app version to use for setup (e.g., v1.1.6)" + required: false + type: string + default: "v1.1.6" + setup_type: + description: "Wallet setup type (standard | sweep)" + required: true + type: string + scenario_name: + description: "Migration scenario name for artifact naming" + required: true + type: string + +env: + TERM: xterm-256color + FORCE_COLOR: 1 + +jobs: + prepare-wallets: + runs-on: ubuntu-latest + + steps: + - name: Clone E2E tests + uses: actions/checkout@v4 + with: + repository: synonymdev/bitkit-e2e-tests + path: bitkit-e2e-tests + ref: ${{ inputs.e2e_branch }} + + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Download RN app for migration + run: | + mkdir -p bitkit-e2e-tests/aut + curl -L -o bitkit-e2e-tests/aut/bitkit_rn_regtest.apk \ + https://github.com/synonymdev/bitkit-e2e-tests/releases/download/migration-rn-regtest/bitkit_rn_regtest_${{ inputs.rn_version }}.apk + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Cache npm cache + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + working-directory: bitkit-e2e-tests + run: npm ci + + - name: Clear previous migration env files + working-directory: bitkit-e2e-tests + run: | + rm -f artifacts/migration_setup_standard.env + rm -f artifacts/migration_setup_sweep.env + + - name: Prepare migration wallet 1 + continue-on-error: true + id: prepare1 + uses: reactivecircus/android-emulator-runner@v2 + with: + profile: pixel_6 + api-level: 33 + arch: x86_64 + avd-name: Pixel_6 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none + script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ inputs.setup_type == 'sweep' && '@migration_setup_sweep' || '@migration_setup_standard' }}" + env: + BACKEND: regtest + + - name: Prepare migration wallet 2 + continue-on-error: true + id: prepare2 + if: steps.prepare1.outcome != 'success' + uses: reactivecircus/android-emulator-runner@v2 + with: + profile: pixel_6 + api-level: 33 + arch: x86_64 + avd-name: Pixel_6 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none + script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ inputs.setup_type == 'sweep' && '@migration_setup_sweep' || '@migration_setup_standard' }}" + env: + BACKEND: regtest + + - name: Prepare migration wallet 3 + id: prepare3 + if: steps.prepare1.outcome != 'success' && steps.prepare2.outcome != 'success' + uses: reactivecircus/android-emulator-runner@v2 + with: + profile: pixel_6 + api-level: 33 + arch: x86_64 + avd-name: Pixel_6 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-front none + script: cd bitkit-e2e-tests && ./ci_run_android.sh --mochaOpts.grep "${{ inputs.setup_type == 'sweep' && '@migration_setup_sweep' || '@migration_setup_standard' }}" + env: + BACKEND: regtest + + - name: Verify migration env file + run: | + set -euo pipefail + if [[ "${{ inputs.setup_type }}" == "sweep" ]]; then + test -f bitkit-e2e-tests/artifacts/migration_setup_sweep.env + else + test -f bitkit-e2e-tests/artifacts/migration_setup_standard.env + fi + + - name: Upload migration env file + uses: actions/upload-artifact@v4 + with: + name: migration-env_${{ inputs.rn_version }}_${{ inputs.scenario_name }} + path: bitkit-e2e-tests/artifacts/migration_setup_${{ inputs.setup_type }}.env From c1c0a6060a65600022d6ba3334122325993882e4 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Mon, 19 Jan 2026 18:16:20 +0100 Subject: [PATCH 2/5] @migration_setup --- test/specs/migration.e2e.ts | 102 +++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/test/specs/migration.e2e.ts b/test/specs/migration.e2e.ts index 1a586cd..21c3157 100644 --- a/test/specs/migration.e2e.ts +++ b/test/specs/migration.e2e.ts @@ -1,3 +1,5 @@ +import fs from 'node:fs'; +import path from 'node:path'; import { acknowledgeReceivedPayment, confirmInputOnKeyboard, @@ -61,6 +63,10 @@ const TEST_PASSPHRASE = 'supersecret'; // This is needed because RN app on iOS has poor Appium support const IOS_RN_MNEMONIC = process.env.RN_MNEMONIC; const IOS_RN_BALANCE = process.env.RN_BALANCE ? parseInt(process.env.RN_BALANCE, 10) : undefined; +const IOS_RN_MNEMONIC_SWEEP = process.env.RN_MNEMONIC_SWEEP; +const IOS_RN_BALANCE_SWEEP = process.env.RN_BALANCE_SWEEP + ? parseInt(process.env.RN_BALANCE_SWEEP, 10) + : undefined; // ============================================================================ // TEST SUITE @@ -76,6 +82,39 @@ describe('@migration - Migration from legacy RN app to native app', () => { await electrumClient?.stop(); }); + // -------------------------------------------------------------------------- + // Migration Setup: Prepare legacy RN wallets on Android for iOS runs + // -------------------------------------------------------------------------- + ciIt('@migration_setup_standard - Prepare legacy RN wallet (Android only)', async () => { + if (driver.isIOS) { + throw new Error('Migration setup should run on Android only.'); + } + + const { mnemonic, balance } = await setupLegacyWallet({ returnSeed: true }); + writeMigrationEnvFile({ + fileName: 'migration_setup_standard.env', + mnemonicVar: 'RN_MNEMONIC', + balanceVar: 'RN_BALANCE', + mnemonic, + balance, + }); + }); + + ciIt('@migration_setup_sweep - Prepare legacy sweep wallet (Android only)', async () => { + if (driver.isIOS) { + throw new Error('Migration setup should run on Android only.'); + } + + const { mnemonic, balance } = await setupWalletWithLegacyFunds({ returnSeed: true }); + writeMigrationEnvFile({ + fileName: 'migration_setup_sweep.env', + mnemonicVar: 'RN_MNEMONIC_SWEEP', + balanceVar: 'RN_BALANCE_SWEEP', + mnemonic, + balance, + }); + }); + // -------------------------------------------------------------------------- // Migration Scenario 1: Uninstall RN, install Native, restore mnemonic // -------------------------------------------------------------------------- @@ -284,6 +323,36 @@ async function setupLegacyWallet( return { mnemonic, balance }; } +type MigrationEnvFileArgs = { + fileName: string; + mnemonicVar: string; + balanceVar: string; + mnemonic?: string; + balance: number; +}; + +function writeMigrationEnvFile({ + fileName, + mnemonicVar, + balanceVar, + mnemonic, + balance, +}: MigrationEnvFileArgs): void { + if (!mnemonic) { + throw new Error(`Missing mnemonic for ${fileName}`); + } + + const filePath = path.join(process.cwd(), 'artifacts', fileName); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + + const contents = `${mnemonicVar}="${mnemonic}"\n${balanceVar}="${balance}"\n`; + fs.writeFileSync(filePath, contents, 'utf8'); + + console.info(`→ Wrote migration env file: ${filePath}`); + console.info(`\nexport ${mnemonicVar}="${mnemonic}"`); + console.info(`export ${balanceVar}="${balance}"\n`); +} + // Amount constants for sweep scenario const SWEEP_INITIAL_FUND_SATS = 200_000; const SWEEP_SEND_TO_SELF_SATS = 50_000; @@ -300,13 +369,42 @@ const SWEEP_SEND_TO_SELF_SATS = 50_000; * * Result: Wallet has funds on legacy address, migration will trigger sweep */ -async function setupWalletWithLegacyFunds(): Promise<{ balance: number }> { +async function setupWalletWithLegacyFunds( + options: { returnSeed?: boolean } = {} +): Promise { + const { returnSeed } = options; + if (driver.isIOS) { + if (!IOS_RN_MNEMONIC_SWEEP || !IOS_RN_BALANCE_SWEEP) { + throw new Error( + 'iOS migration sweep tests require RN_MNEMONIC_SWEEP and RN_BALANCE_SWEEP env vars. ' + + 'Run Android setup first to prepare the wallet.' + ); + } + console.info('=== iOS: Restoring RN sweep wallet from mnemonic (prepared by Android) ==='); + console.info( + `→ Mnemonic: ${IOS_RN_MNEMONIC_SWEEP.split(' ').slice(0, 3).join(' ')}...` + ); + console.info(`→ Expected balance: ${IOS_RN_BALANCE_SWEEP} sats`); + + await installLegacyRnApp(); + await restoreRnWallet(IOS_RN_MNEMONIC_SWEEP); + + console.info('=== iOS: RN sweep wallet restored ==='); + return { mnemonic: IOS_RN_MNEMONIC_SWEEP, balance: IOS_RN_BALANCE_SWEEP }; + } + console.info('=== Setting up wallet with legacy funds (sweep scenario) ==='); // Install and create wallet await installLegacyRnApp(); await createLegacyRnWallet(); + let mnemonic: string | undefined; + if (returnSeed) { + mnemonic = await getRnMnemonic(); + console.info(`→ Legacy RN sweep wallet mnemonic: ${mnemonic}`); + } + // 1. Fund wallet on native segwit (works with Blocktank) console.info('→ Step 1: Funding wallet on native segwit...'); await fundRnWallet(SWEEP_INITIAL_FUND_SATS); @@ -324,7 +422,7 @@ async function setupWalletWithLegacyFunds(): Promise<{ balance: number }> { console.info('=== Legacy funds setup complete ==='); - return { balance }; + return { balance, mnemonic }; } /** From 97b0df244b84f30bd9e5fb7a0d8bacc9ac6f5ed2 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Thu, 22 Jan 2026 13:14:17 +0100 Subject: [PATCH 3/5] use pasteios --- test/helpers/actions.ts | 2 +- test/specs/migration.e2e.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index 1083605..55f7232 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -282,7 +282,7 @@ export async function multiTap(testId: string, count: number) { } } -async function pasteIOSText(testId: string, text: string) { +export async function pasteIOSText(testId: string, text: string) { if (!driver.isIOS) { throw new Error('pasteIOSText can only be used on iOS devices'); } diff --git a/test/specs/migration.e2e.ts b/test/specs/migration.e2e.ts index 21c3157..22209b3 100644 --- a/test/specs/migration.e2e.ts +++ b/test/specs/migration.e2e.ts @@ -14,6 +14,7 @@ import { getReceiveAddress, getUriFromQRCode, handleAndroidAlert, + pasteIOSText, restoreWallet, sleep, swipeFullScreen, @@ -594,7 +595,11 @@ async function restoreRnWallet( await tap('MultipleDevices-button'); // Enter seed - await typeText('Word-0', mnemonic); + if (driver.isIOS) { + await pasteIOSText('Word-0', mnemonic); + } else { + await typeText('Word-0', mnemonic); + } await sleep(1500); // Passphrase if provided From bef9f468730518daa288db3af89d0479c2811ff2 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Thu, 22 Jan 2026 16:12:46 +0100 Subject: [PATCH 4/5] @migration_ios --- test/specs/migration.e2e.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/specs/migration.e2e.ts b/test/specs/migration.e2e.ts index 22209b3..5c57777 100644 --- a/test/specs/migration.e2e.ts +++ b/test/specs/migration.e2e.ts @@ -116,6 +116,13 @@ describe('@migration - Migration from legacy RN app to native app', () => { }); }); + ciIt('@migration_ios - setupLegacyWallet on iOS', async () => { + // Setup wallet in RN app + const { mnemonic, balance } = await setupLegacyWallet({ returnSeed: true }); + console.info(`→ MNEMONIC: ${mnemonic}`); + console.info(`→ BALANCE: ${balance}`); + }); + // -------------------------------------------------------------------------- // Migration Scenario 1: Uninstall RN, install Native, restore mnemonic // -------------------------------------------------------------------------- From 2188298d53f8b2e3514e7d60bdceea885062a8dc Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 27 Jan 2026 21:40:11 +0100 Subject: [PATCH 5/5] symlink --- .github/workflows/migration-wallet-setup.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/migration-wallet-setup.yml b/.github/workflows/migration-wallet-setup.yml index 5ca042b..73edf37 100644 --- a/.github/workflows/migration-wallet-setup.yml +++ b/.github/workflows/migration-wallet-setup.yml @@ -48,6 +48,8 @@ jobs: mkdir -p bitkit-e2e-tests/aut curl -L -o bitkit-e2e-tests/aut/bitkit_rn_regtest.apk \ https://github.com/synonymdev/bitkit-e2e-tests/releases/download/migration-rn-regtest/bitkit_rn_regtest_${{ inputs.rn_version }}.apk + # Symlink to bitkit_e2e.apk so wdio.conf.ts can initialize the Appium session + cd bitkit-e2e-tests/aut && ln -sf bitkit_rn_regtest.apk bitkit_e2e.apk - name: Setup Node.js uses: actions/setup-node@v4