Full Code of MutinyWallet/mutiny-web for AI

master 71d1beda6429 cached
250 files
18.8 MB
285.7k tokens
504 symbols
1 requests
Download .txt
Showing preview only (1,284K chars total). Download the full file or copy to clipboard to get everything.
Repository: MutinyWallet/mutiny-web
Branch: master
Commit: 71d1beda6429
Files: 250
Total size: 18.8 MB

Directory structure:
gitextract_b8zdsqe2/

├── .dockerignore
├── .eslintrc.cjs
├── .github/
│   └── workflows/
│       ├── android-build.yml
│       ├── android-prod.yml
│       ├── android-staging.yml
│       ├── code-quality.yml
│       ├── docker.yml
│       ├── ios-prod.yml
│       ├── ios-staging.yml
│       └── playwright.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── android/
│   ├── .gitignore
│   ├── app/
│   │   ├── .gitignore
│   │   ├── build.gradle
│   │   ├── capacitor.build.gradle
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── androidTest/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── getcapacitor/
│   │       │               └── myapp/
│   │       │                   └── ExampleInstrumentedTest.java
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── java/
│   │       │   │   └── com/
│   │       │   │       └── mutinywallet/
│   │       │   │           └── mutinywallet/
│   │       │   │               └── MainActivity.java
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   └── ic_launcher_background.xml
│   │       │       ├── drawable-v24/
│   │       │       │   └── ic_launcher_foreground.xml
│   │       │       ├── layout/
│   │       │       │   └── activity_main.xml
│   │       │       ├── mipmap-anydpi-v26/
│   │       │       │   ├── ic_launcher.xml
│   │       │       │   └── ic_launcher_round.xml
│   │       │       ├── values/
│   │       │       │   ├── ic_launcher_background.xml
│   │       │       │   ├── strings.xml
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           └── file_paths.xml
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── getcapacitor/
│   │                       └── myapp/
│   │                           └── ExampleUnitTest.java
│   ├── build.gradle
│   ├── capacitor.settings.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   ├── settings.gradle
│   └── variables.gradle
├── capacitor.config.ts
├── default.conf
├── e2e/
│   ├── encrypt.spec.ts
│   ├── fedimint.spec.ts
│   ├── load.spec.ts
│   ├── restore.spec.ts
│   ├── roundtrip.spec.ts
│   ├── routes.spec.ts
│   └── utils.ts
├── flake.nix
├── index.html
├── ios/
│   ├── .gitignore
│   └── App/
│       ├── App/
│       │   ├── App.entitlements
│       │   ├── AppDelegate.swift
│       │   ├── Assets.xcassets/
│       │   │   ├── AppIcon.appiconset/
│       │   │   │   └── Contents.json
│       │   │   ├── Contents.json
│       │   │   └── Splash.imageset/
│       │   │       └── Contents.json
│       │   ├── Base.lproj/
│       │   │   ├── LaunchScreen.storyboard
│       │   │   └── Main.storyboard
│       │   └── Info.plist
│       ├── App.ipa
│       ├── App.xcodeproj/
│       │   ├── project.pbxproj
│       │   ├── project.xcworkspace/
│       │   │   └── contents.xcworkspacedata
│       │   └── xcshareddata/
│       │       └── xcschemes/
│       │           └── App.xcscheme
│       ├── App.xcworkspace/
│       │   ├── contents.xcworkspacedata
│       │   └── xcshareddata/
│       │       └── IDEWorkspaceChecks.plist
│       ├── Gemfile
│       ├── MyCustomViewController.swift
│       ├── Podfile
│       └── fastlane/
│           ├── .gitignore
│           ├── Appfile
│           ├── Fastfile
│           ├── Matchfile
│           └── README.md
├── justfile
├── manifest.ts
├── package.json
├── playwright.config.ts
├── postcss.config.cjs
├── prettier.config.mjs
├── public/
│   ├── .well-known/
│   │   ├── apple-app-site-association
│   │   └── assetlinks.json
│   ├── i18n/
│   │   ├── de.json
│   │   ├── en.json
│   │   ├── es.json
│   │   ├── fr.json
│   │   ├── hans.json
│   │   ├── hant.json
│   │   ├── it.json
│   │   ├── ko.json
│   │   └── pt.json
│   └── robots.txt
├── scripts/
│   └── errorsToTs.cjs
├── src/
│   ├── components/
│   │   ├── Activity.tsx
│   │   ├── ActivityDetailsModal.tsx
│   │   ├── Amount.tsx
│   │   ├── AmountEditable.tsx
│   │   ├── BalanceBox.tsx
│   │   ├── BigMoney.tsx
│   │   ├── ChooseCurrency.tsx
│   │   ├── ChooseLanguage.tsx
│   │   ├── ContactButton.tsx
│   │   ├── ContactEditor.tsx
│   │   ├── ContactForm.tsx
│   │   ├── ContactViewer.tsx
│   │   ├── DecryptDialog.tsx
│   │   ├── DeleteEverything.tsx
│   │   ├── EditProfileForm.tsx
│   │   ├── ErrorDisplay.tsx
│   │   ├── Fab.tsx
│   │   ├── Failure.tsx
│   │   ├── FederationInviteShower.tsx
│   │   ├── FederationPopup.tsx
│   │   ├── Fee.tsx
│   │   ├── FeeDisplay.tsx
│   │   ├── GenericItem.tsx
│   │   ├── HomeBalance.tsx
│   │   ├── HomePrompt.tsx
│   │   ├── HomeSubnav.tsx
│   │   ├── I18nProvider.tsx
│   │   ├── IOSbanner.tsx
│   │   ├── ImportExport.tsx
│   │   ├── ImportNsecForm.tsx
│   │   ├── InfoBox.tsx
│   │   ├── IntegratedQR.tsx
│   │   ├── JsonModal.tsx
│   │   ├── KitchenSink.tsx
│   │   ├── LabelCircle.tsx
│   │   ├── LightningAddressShower.tsx
│   │   ├── LoadingIndicator.tsx
│   │   ├── Logo.tsx
│   │   ├── Logs.tsx
│   │   ├── MoreInfoModal.tsx
│   │   ├── MutinyPlusCta.tsx
│   │   ├── NWCEditor.tsx
│   │   ├── NavBar.tsx
│   │   ├── NostrActivity.tsx
│   │   ├── PendingNwc.tsx
│   │   ├── Reader.tsx
│   │   ├── ReceiveWarnings.tsx
│   │   ├── Reload.tsx
│   │   ├── Restart.tsx
│   │   ├── ResyncOnchain.tsx
│   │   ├── SeedWords.tsx
│   │   ├── SetupErrorDisplay.tsx
│   │   ├── ShareCard.tsx
│   │   ├── SharpButton.tsx
│   │   ├── ShutdownPopup.tsx
│   │   ├── SimpleInput.tsx
│   │   ├── SocialActionRow.tsx
│   │   ├── Toaster.tsx
│   │   ├── ToggleHodl.tsx
│   │   ├── index.ts
│   │   ├── layout/
│   │   │   ├── BackLink.tsx
│   │   │   ├── BackPop.tsx
│   │   │   ├── Button.tsx
│   │   │   ├── ExternalLink.tsx
│   │   │   ├── LoadingSpinner.tsx
│   │   │   ├── Misc.tsx
│   │   │   ├── Radio.tsx
│   │   │   ├── SubtleButton.tsx
│   │   │   ├── TextField.tsx
│   │   │   └── index.ts
│   │   └── successfail/
│   │       ├── MegaCheck.tsx
│   │       ├── MegaClock.tsx
│   │       ├── MegaEx.tsx
│   │       ├── SuccessModal.tsx
│   │       └── index.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── context.ts
│   ├── index.tsx
│   ├── logic/
│   │   ├── browserCompatibility.ts
│   │   ├── errorDispatch.ts
│   │   ├── mutinyWalletSetup.ts
│   │   └── waila.ts
│   ├── root.css
│   ├── router.tsx
│   ├── routes/
│   │   ├── Chat.tsx
│   │   ├── EditProfile.tsx
│   │   ├── Feedback.tsx
│   │   ├── Main.tsx
│   │   ├── Profile.tsx
│   │   ├── Receive.tsx
│   │   ├── Redeem.tsx
│   │   ├── Request.tsx
│   │   ├── Scanner.tsx
│   │   ├── Search.tsx
│   │   ├── Send.tsx
│   │   ├── Swap.tsx
│   │   ├── SwapLightning.tsx
│   │   ├── Transfer.tsx
│   │   ├── [...404].tsx
│   │   ├── index.ts
│   │   ├── settings/
│   │   │   ├── Admin.tsx
│   │   │   ├── Backup.tsx
│   │   │   ├── Channels.tsx
│   │   │   ├── Connections.tsx
│   │   │   ├── Currency.tsx
│   │   │   ├── EmergencyKit.tsx
│   │   │   ├── Encrypt.tsx
│   │   │   ├── ImportProfile.tsx
│   │   │   ├── Language.tsx
│   │   │   ├── LightningAddress.tsx
│   │   │   ├── ManageFederations.tsx
│   │   │   ├── NostrKeys.tsx
│   │   │   ├── Plus.tsx
│   │   │   ├── Restore.tsx
│   │   │   ├── Root.tsx
│   │   │   ├── Servers.tsx
│   │   │   └── index.ts
│   │   └── setup/
│   │       ├── AddFederation.tsx
│   │       ├── ImportProfile.tsx
│   │       ├── NewProfile.tsx
│   │       ├── Restore.tsx
│   │       ├── Root.tsx
│   │       └── index.ts
│   ├── state/
│   │   └── megaStore.tsx
│   ├── styles/
│   │   └── dialogs.ts
│   ├── utils/
│   │   ├── baseUrl.ts
│   │   ├── bech32.ts
│   │   ├── blobToBase64.ts
│   │   ├── conversions.ts
│   │   ├── currencies.ts
│   │   ├── debounce.ts
│   │   ├── deepSignal.ts
│   │   ├── download.ts
│   │   ├── eify.ts
│   │   ├── fetchZaps.ts
│   │   ├── gradientHash.ts
│   │   ├── index.ts
│   │   ├── keypad.ts
│   │   ├── languages.ts
│   │   ├── mempoolTxUrl.ts
│   │   ├── nostr.ts
│   │   ├── objectToSearchParams.ts
│   │   ├── openLinkProgrammatically.ts
│   │   ├── platform.ts
│   │   ├── prettyPrintTime.ts
│   │   ├── subscriptions.ts
│   │   ├── timeout.ts
│   │   ├── typescript.ts
│   │   ├── useCopy.ts
│   │   ├── vibrate.ts
│   │   └── words.ts
│   ├── vite-env.d.ts
│   └── workers/
│       └── walletWorker.ts
├── tailwind.config.cjs
├── tsconfig.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
node_modules
.gitignore
.github
*.md
dist
justfile
.env.local


================================================
FILE: .eslintrc.cjs
================================================
module.exports = {
    env: {
        browser: true,
        es2021: true,
        node: true
    },
    extends: [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:solid/typescript",
        "plugin:import/typescript",
        "plugin:import/recommended"
    ],
    overrides: [
        {
            files: ["**/*.cjs"], // Specify the file pattern for CJS files
            parserOptions: {
                sourceType: "script" // Treat the CJS files as CommonJS modules
            },
            rules: {
                "@typescript-eslint/no-var-requires": "off" // Disable this specific rule for CJS files
            }
        }
    ],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        tsconfigRootDir: "./",
        project: "tsconfig.json",
        ecmaVersion: "latest",
        sourceType: "module",
        ecmaFeatures: {
            jsx: true
        }
    },
    plugins: ["@typescript-eslint", "solid", "import"],
    rules: {
        "@typescript-eslint/no-unused-vars": [
            "warn",
            {
                argsIgnorePattern: "^_",
                destructuredArrayIgnorePattern: "^_",
                varsIgnorePattern: "^_"
            }
        ],
        "solid/reactivity": "warn",
        "solid/no-destructure": "warn",
        "solid/jsx-no-undef": "error",
        "@typescript-eslint/no-non-null-assertion": "off"
    },
    settings: {
        "import/parsers": {
            "@typescript-eslint/parser": [".ts", ".tsx"]
        },
        "import/resolver": {
            typescript: {
                project: ["./tsconfig.json"],
                alwaysTryTypes: true
            }
        }
    }
};


================================================
FILE: .github/workflows/android-build.yml
================================================
name: Build Android

on:
  push:
    branches:
      - master
      - prod
  pull_request:
    branches:
      - '*'

jobs:
  build:
    name: Build APK
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Setup java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Cache gradle
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        id: pnpm-install
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install app dependencies
        run: pnpm install

      - name: Build SolidJS app
        env:
          VITE_NETWORK: signet
          VITE_PROXY: wss://p.mutinywallet.com
          VITE_ESPLORA: https://mutinynet.com/api
          VITE_LSP: https://mutinynet-flow.lnolymp.us
          VITE_RGS: https://rgs.mutinynet.com/snapshot/
          VITE_AUTH: https://auth-staging.mutinywallet.com
          VITE_SUBSCRIPTIONS: https://subscriptions-staging.mutinywallet.com
          VITE_STORAGE: https://storage-staging.mutinywallet.com/v2
          VITE_FEEDBACK: https://feedback-staging.mutinywallet.com
          VITE_SCORER: https://scorer-staging.mutinywallet.com
          VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
        run: pnpm build

      - name: Capacitor sync
        run: npx cap sync

      - name: Build APK (gradle)
        working-directory: android
        run: ./gradlew assembleDebug --no-daemon

      - name: Upload APK
        uses: actions/upload-artifact@v2
        with:
          name: Debug APK
          path: android/app/build/outputs/apk/fdroid/debug/app-fdroid-universal-debug.apk


================================================
FILE: .github/workflows/android-prod.yml
================================================
name: Release Android Prod

on:
  push:
    tags:
    - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10

jobs:
  build:
    name: Build APK
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Setup java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Cache gradle
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        id: pnpm-install
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install app dependencies
        run: pnpm install

      - name: Build SolidJS app
        env:
          VITE_NETWORK: bitcoin
          VITE_PROXY: wss://p.mutinywallet.com
          VITE_ESPLORA: https://mutiny.mempool.space/api
          VITE_LSP: https://0conf.lnolymp.us
          VITE_RGS: https://scorer.mutinywallet.com/v1/rgs/snapshot/
          VITE_AUTH: https://auth.mutinywallet.com
          VITE_SUBSCRIPTIONS: https://subscriptions.mutinywallet.com
          VITE_STORAGE: https://storage.mutinywallet.com/v2
          VITE_FEEDBACK: https://feedback.mutinywallet.com
          VITE_SCORER: https://scorer.mutinywallet.com
          VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
          VITE_BLIND_AUTH: https://blind-auth.mutinywallet.com
          VITE_HERMES: https://mutiny.plus
        run: pnpm build

      - name: Capacitor sync
        run: npx cap sync

      - name: Build AAB
        working-directory: android
        run: ./gradlew clean bundleRelease --stacktrace

      - name: Sign AAB (F-Droid)
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: android/app/build/outputs/bundle/fdroidRelease
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.KEY_ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
        env:
          BUILD_TOOLS_VERSION: "33.0.0"

      - name: Build APK
        working-directory: android
        run: ./gradlew assembleRelease --stacktrace

      - name: Sign APK (F-Droid)
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: android/app/build/outputs/apk/fdroid/release
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.KEY_ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
        env:
          BUILD_TOOLS_VERSION: "33.0.0"

      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Android Release ${{ github.ref }}
          draft: false
          prerelease: false 

      # F-Droid APK
      - name: Upload F-Droid APK Universal Asset
        id: upload-release-asset-fdroid-universal-apk
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: android/app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk
          asset_name: mutiny-wallet-fdroid-universal-${{ github.ref_name }}.apk
          asset_content_type: application/zip

      - name: Upload F-Droid APK x86 Asset
        id: upload-release-asset-fdroid-x86-apk
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: android/app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk
          asset_name: mutiny-wallet-fdroid-x86-${{ github.ref_name }}.apk
          asset_content_type: application/zip

      - name: Upload F-Droid APK x86_64 Asset
        id: upload-release-asset-fdroid-x86-64-apk
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: android/app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk
          asset_name: mutiny-wallet-fdroid-x86_64-${{ github.ref_name }}.apk
          asset_content_type: application/zip

      # FDroid AAB
      - name: Upload F-Droid AAB Asset
        id: upload-release-asset-fdroid-aab
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: android/app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab
          asset_name: mutiny-wallet-fdroid-${{ github.ref_name }}.aab
          asset_content_type: application/zip


================================================
FILE: .github/workflows/android-staging.yml
================================================
name: Release Android Staging

on:
  workflow_dispatch:
  push:
    branches:
      - master

jobs:
  build:
    name: Build APK
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Setup java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Cache gradle
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        id: pnpm-install
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install app dependencies
        run: pnpm install

      - name: Build SolidJS app
        env:
          VITE_NETWORK: signet
          VITE_PROXY: wss://p.mutinywallet.com
          VITE_ESPLORA: https://mutinynet.com/api
          VITE_LSP: https://mutinynet-flow.lnolymp.us
          VITE_RGS: https://rgs.mutinynet.com/snapshot/
          VITE_AUTH: https://auth-staging.mutinywallet.com
          VITE_SUBSCRIPTIONS: https://subscriptions-staging.mutinywallet.com
          VITE_STORAGE: https://storage-staging.mutinywallet.com/v2
          VITE_FEEDBACK: https://feedback-staging.mutinywallet.com
          VITE_SCORER: https://scorer-staging.mutinywallet.com
          VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
          VITE_BLIND_AUTH: https://blind-auth-staging.mutinywallet.com
          VITE_HERMES: https://signet.mutiny.plus
        run: pnpm build

      - name: Capacitor sync
        run: npx cap sync

      - name: Build AAB
        working-directory: android
        run: ./gradlew clean bundleRelease --stacktrace

      - name: Sign AAB (F-Droid)
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: android/app/build/outputs/bundle/fdroidRelease
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.KEY_ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
        env:
          BUILD_TOOLS_VERSION: "33.0.0"

      - name: Build APK
        working-directory: android
        run: ./gradlew assembleRelease --stacktrace

      - name: Sign APK (F-Droid)
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: android/app/build/outputs/apk/fdroid/release
          signingKeyBase64: ${{ secrets.SIGNING_KEY }}
          alias: ${{ secrets.KEY_ALIAS }}
          keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
        env:
          BUILD_TOOLS_VERSION: "33.0.0"

      # F-Droid APK
      - name: Upload F-Droid APK Universal Asset
        id: upload-release-asset-fdroid-universal-apk
        uses: actions/upload-artifact@v3
        with:
          path: android/app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk
          name: mutiny-wallet-fdroid-universal-master.apk

      - name: Upload F-Droid APK x86 Asset
        id: upload-release-asset-fdroid-x86-apk
        uses: actions/upload-artifact@v3
        with:
          path: android/app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk
          name: mutiny-wallet-fdroid-x86-master.apk

      - name: Upload F-Droid APK x86_64 Asset
        id: upload-release-asset-fdroid-x86-64-apk
        uses: actions/upload-artifact@v3
        with:
          path: android/app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk
          name: mutiny-wallet-fdroid-x86_64-master.apk

      # FDroid AAB
      - name: Upload F-Droid AAB Asset
        uses: actions/upload-artifact@v3
        with:
          path: android/app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab
          name: mutiny-wallet-fdroid-master.aab


================================================
FILE: .github/workflows/code-quality.yml
================================================
name: Code Quality Check
on:
  push:
    branches:
      - master
      - prod
  pull_request:
    branches:
      - '*'

jobs:
  code_quality:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        id: pnpm-install
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Step Zero - Install Dependencies
        run: pnpm install

      - name: Step One - Check Code Formatting
        run: pnpm run check-format

      - name: Step Two - Check Lint Errors
        run: pnpm run eslint

      - name: Step Three - Check Type Check Errors
        run: pnpm run check-types



================================================
FILE: .github/workflows/docker.yml
================================================
name: Create and publish Docker images

on:
  workflow_dispatch:
  push:
    branches:
      - master
  release:
    types: [ published ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to the Container registry
        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
        with:
          registry: ${{ env.REGISTRY }}
          username: mutinywallet
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v3
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/ios-prod.yml
================================================
name: Release iOS Prod

on:
    workflow_dispatch:
    push:
        branches:
        - master

jobs:
  build:
    name: Build iOS
    runs-on: macos-14
    timeout-minutes: 180
    steps:
        - name: Checkout source
          uses: actions/checkout@v3

        - uses: pnpm/action-setup@v4
          name: Install pnpm
          id: pnpm-install
          with:
            run_install: false

        - name: Setup Node.js
          uses: actions/setup-node@v3
          with:
            node-version: 18.x
            cache: 'pnpm'

        # Install dependencies using pnpm
        - name: Install Dependencies
          run: pnpm install

        - name: Build SolidJS app
          env:
            VITE_NETWORK: bitcoin
            VITE_PROXY: wss://p.mutinywallet.com
            VITE_ESPLORA: https://mutiny.mempool.space/api
            VITE_LSP: https://0conf.lnolymp.us
            VITE_RGS: https://scorer.mutinywallet.com/v1/rgs/snapshot/
            VITE_AUTH: https://auth.mutinywallet.com
            VITE_SUBSCRIPTIONS: https://subscriptions.mutinywallet.com
            VITE_STORAGE: https://storage.mutinywallet.com/v2
            VITE_FEEDBACK: https://feedback.mutinywallet.com
            VITE_SCORER: https://scorer.mutinywallet.com
            VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
            VITE_BLIND_AUTH: https://blind-auth.mutinywallet.com
            VITE_HERMES: https://mutiny.plus
          run: pnpm build

        - uses: actions/cache@v3
          with:
            path: ios/App/Pods
            key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
            restore-keys: |
              ${{ runner.os }}-pods-

        - name: Capacitor sync
          run: npx cap sync
        
        - uses: ruby/setup-ruby@v1
          with:
            ruby-version: '3.3'
            bundler-cache: true
            working-directory: 'ios/App'

        - uses: maxim-lobanov/setup-xcode@v1
          with:
            xcode-version: '15.2'

        - uses: maierj/fastlane-action@v3.0.0
          env:
            DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
            KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
            KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
            APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
            APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
            APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
            CERTIFICATE_STORE_URL: ${{ secrets.CERTIFICATE_STORE_URL }}
            GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
            GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
            FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
            MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
            MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
            APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
            DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
          with:
            lane: 'beta'
            subdirectory: 'ios/App'




================================================
FILE: .github/workflows/ios-staging.yml
================================================
name: Build iOS Staging

on:
  workflow_dispatch:
  pull_request:
    branches:
      - '*'

jobs:
  build:
    name: Build iOS
    runs-on: macos-14
    timeout-minutes: 180
    steps:
        - name: Checkout source
          uses: actions/checkout@v3

        - uses: pnpm/action-setup@v4
          name: Install pnpm
          id: pnpm-install
          with:
            run_install: false

        - name: Setup Node.js
          uses: actions/setup-node@v3
          with:
            node-version: 18.x
            cache: 'pnpm'

        # Install dependencies using pnpm
        - name: Install Dependencies
          run: pnpm install

        - name: Build SolidJS app
          env:
            VITE_NETWORK: signet
            VITE_PROXY: wss://p.mutinywallet.com
            VITE_ESPLORA: https://mutinynet.com/api
            VITE_LSP: https://mutinynet-flow.lnolymp.us
            VITE_RGS: https://rgs.mutinynet.com/snapshot/
            VITE_AUTH: https://auth-staging.mutinywallet.com
            VITE_SUBSCRIPTIONS: https://subscriptions-staging.mutinywallet.com
            VITE_STORAGE: https://storage-staging.mutinywallet.com/v2
            VITE_FEEDBACK: https://feedback-staging.mutinywallet.com
            VITE_SCORER: https://scorer-staging.mutinywallet.com
            VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
            VITE_BLIND_AUTH: https://blind-auth-staging.mutinywallet.com
            VITE_HERMES: https://signet.mutiny.plus
          run: pnpm build

        - uses: actions/cache@v3
          with:
            path: ios/App/Pods
            key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
            restore-keys: |
              ${{ runner.os }}-pods-

        - name: Capacitor sync
          run: npx cap sync
        
        - uses: ruby/setup-ruby@v1
          with:
            ruby-version: '3.3'
            bundler-cache: true
            working-directory: 'ios/App'

        - uses: maxim-lobanov/setup-xcode@v1
          with:
            xcode-version: '15.2'

        - uses: maierj/fastlane-action@v3.0.0
          env:
            DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
            # PROVISIONING_PROFILE_SPECIFIER: match AppStore ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
            KEYCHAIN_NAME: ${{ secrets.KEYCHAIN_NAME }}
            KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
            APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
            APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
            APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
            CERTIFICATE_STORE_URL: ${{ secrets.CERTIFICATE_STORE_URL }}
            GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
            GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
            FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
            MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
            MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
            APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
            DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
          with:
            lane: 'build'
            subdirectory: 'ios/App'




================================================
FILE: .github/workflows/playwright.yml
================================================
name: Playwright Tests

# Only run one at a time per branch
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

on:
  pull_request:
    branches:
      - '*'
      
jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x

      - uses: pnpm/action-setup@v4
        name: Install pnpm
        id: pnpm-install
        with:
          run_install: false

      - name: Get pnpm store directory
        id: pnpm-cache
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

      - uses: actions/cache@v3
        name: Setup pnpm cache
        with:
          path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install app dependencies
        run: pnpm install

      - run: npx playwright install --with-deps
        if: steps.playwright-cache.outputs.cache-hit != 'true'
      - run: npx playwright install-deps
        if: steps.playwright-cache.outputs.cache-hit != 'true'

      - name: Run Playwright tests
        env:
          VITE_NETWORK: signet
          VITE_PROXY: wss://p.mutinywallet.com
          VITE_ESPLORA: https://mutinynet.com/api
          VITE_LSP: https://mutinynet-flow.lnolymp.us
          VITE_RGS: https://rgs.mutinynet.com/snapshot/
          VITE_AUTH: https://auth-staging.mutinywallet.com
          VITE_SUBSCRIPTIONS: https://subscriptions-staging.mutinywallet.com
          VITE_STORAGE: https://storage-staging.mutinywallet.com/v2
          VITE_FEEDBACK: https://feedback-staging.mutinywallet.com
          VITE_SCORER: https://scorer-staging.mutinywallet.com
          VITE_PRIMAL: https://primal-cache.mutinywallet.com/api
        run: pnpm exec playwright test --grep-invert @slow
      - uses: actions/upload-artifact@v3
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local
android/app/play/*
android/app/fdroid/*

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.turbo

# PWA dev stuff
dev-dist
.solid
/test-results/
/tests-examples/
/playwright-report/
/playwright/.cache/

.env


================================================
FILE: Dockerfile
================================================
# Use Node.js as a base image for building the site
FROM node:20-slim AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

# This is the cooler way to run pnpm these days (no need to npm install it)
RUN corepack enable
COPY . /app
WORKDIR /app

# I think we need git be here because the vite build wants to look up the commit hash
RUN apt update && apt install -y git python3 make build-essential

# Add the ARG directives for build-time environment variables
ARG VITE_NETWORK="bitcoin"
ARG VITE_PROXY="/_services/proxy"
ARG VITE_PRIMAL="https://primal-cache.mutinywallet.com/api"
ARG VITE_ESPLORA
ARG VITE_SCORER="https://scorer.mutinywallet.com"
ARG VITE_LSP="https://0conf.lnolymp.us"
ARG VITE_RGS
ARG VITE_AUTH
ARG VITE_STORAGE="/_services/vss/v2"
ARG VITE_SELFHOSTED="true"

# Install dependencies
RUN pnpm install --frozen-lockfile

# IDK why but it gets mad if you don't do this
RUN git config --global --add safe.directory /app

# Build the static site
RUN pnpm run build

# Now, use Nginx as a base image for serving the site
FROM nginx:alpine

# Copy the static assets from the builder stage to the Nginx default static serve directory
COPY --from=builder /app/dist/public /usr/share/nginx/html

# Copy the custom Nginx configuration file into the container
COPY default.conf /etc/nginx/conf.d/default.conf

# Expose the default Nginx port
EXPOSE 80

# Start Nginx when the container starts
CMD ["nginx", "-g", "daemon off;"]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022-2023 Mutiny Wallet Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
### Running Mutiny Web

### Dependencies

-   pnpm > 8

```
pnpm install
pnpm run dev
```

### Env

The easiest way to get start with development is to create a file called `.env.local` and copy the contents of `.env.example` into it. This is basically identical to the env that `signet-app.mutinywallet.com` uses.

### Testing

We have a couple Playwright e2e tests in the e2e folder. You can run these with:

```
just test
```

Or get a visual look into what's happening:

```
just test-ui
```

### Formatting

Hopefully your editor picks up on the `prettier.config.mjs` file and auto formats accordingly. If you want to format everything in the project run `pnpm run format`.

### Deploying Web

Create a PR from `master` to `prod`, and once it does CI and gets approvals, do this from the command line:

```
git checkout master && git pull && git checkout prod && git pull && git merge --ff-only origin/master && git push
```

## Contributing

Before committing make sure to run `pnpm run pre-commit`. This will typecheck, lint, and format everything so CI won't hassle you. (Shortcut: `just pre`).

### Local

If you want to develop against a local version of [the node manager](https://github.com/MutinyWallet/mutiny-node), you may want to `pnpm link` it.

Due to how [Vite's dev server works](https://vitejs.dev/config/server-options.html#server-fs-allow), the linked `mutiny-node` project folder should be a sibling of this `mutiny-web` folder. Alternatively you can change the allow path in `vite.config.ts`.

In your `mutiny-node` local repo:

```
just link
```

(on a Mac you might need to prefix `just link` with these flags: `AR=/opt/homebrew/opt/llvm/bin/llvm-ar CC=/opt/homebrew/opt/llvm/bin/clang`)

Now in this repo, link them.

```
just local
```

To revert back and use the remote version of mutiny-wasm:

```
just remote
```

## Android

### How to test locally

```
just native
```

Now open up the `android` directory in android studio and run the build

### Deploying

#### Pull Requests

Each pull request will build the debug signet app and upload it internally to the github actions run.

#### Master

Each push to master will build a signed release version running in signet mode. The build process is almost identical to the release version.

Prereleased tags will be created for master.

#### Release

##### Android

First bump up the `versionCode` and `versionName` in `./andriod/app/build.gradle`. The `versionCode` must always go up by one when making a release. The `versionName` can mimic `package.json` with an extra build number like `0.4.3-1` to make it easier to keep things looking like they are in sync when android only releases go out.

Publish a new tag like `0.4.3-1` in order to trigger a signed release version running in mainnet mode.

##### iOS

In `ios/App/App.xcodeproj/project.pbxproj` bump `MARKETING_VERSION` and then do whatever needs to be done in testflight to get it released.

### Creating keys for the first time

1. Generate a new signing key

```
keytool -genkey -v -keystore <my-release-key.keystore> -alias <alias_name> -keyalg RSA -keysize 2048 -validity 10000
openssl base64 < <my-release-key.keystore> | tr -d '\n' | tee some_signing_key.jks.base64.txt
```

2. Create 3 Secret Key variables on your GitHub repository and fill in with the signing key information
    - `KEY_ALIAS` <- `<alias_name>`
    - `KEY_STORE_PASSWORD` <- `<your key store password>`
    - `SIGNING_KEY` <- the data from `<my-release-key.keystore>`
3. Change the `versionCode` and `versionName` on `app/build.gradle`
4. Commit and push.

## Translating

### Testing language keys

To check what keys are missing from your desired language:

```
just i18n $lang
```

### Adding new languages or keys

1. In `public/i18n/` locate your desired language .json file or create one if one does not exist

    - When creating a new language file ensure it follows the ISO 639 2-letter standard

2. Populate your translation file with a translation object where all of the keys will be located

If you want to add Japanese you will create a file `/public/i18n/jp.json` and populate it with keys like so:

```
{
  "common": {
        "continue": "続ける",
        ...
    }
}
```

(You should compare your translations against the English language as all other languages are not the master and are likely deprecated)

If you're using VS Code there are some nice extensions that can make this easier like i18n-ally and i18n-json-editor

3. Add your language to the `Language` object in `/src/utils/languages.ts`. This will allow you to select the language via the language selector in the UI. If your desired language is set as your primary language in your browser it will be selected automatically

```
export const LANGUAGE_OPTIONS: Language[] = [
    {
        value: "日本語",
        shortName: "jp"
    },
```

4. That's it! You should now be able to see your translation keys populating the app in your desired language. When youre ready go ahead and open a PR to have you language merged for others!


================================================
FILE: android/.gitignore
================================================
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore

# Built application files
*.apk
*.aar
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/
#  Uncomment the following line in case you need and you don't have the release build type files in your app
# release/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
.cxx/

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/

# Android Profiling
*.hprof

# Cordova plugins for Capacitor
capacitor-cordova-android-plugins

# Copied web assets
app/src/main/assets/public

# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml


================================================
FILE: android/app/.gitignore
================================================
/build/*
!/build/.npmkeep


================================================
FILE: android/app/build.gradle
================================================
apply plugin: 'com.android.application'

android {
    namespace "com.mutinywallet.mutinywallet"
    compileSdk rootProject.ext.compileSdkVersion
    defaultConfig {
        applicationId "com.mutinywallet.mutinywallet"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 76
        versionName "1.8.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        aaptOptions {
             // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
             // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
            ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    flavorDimensions "channel"
    productFlavors {
        play {
            dimension "channel"
        }

        fdroid {
            getIsDefault().set(true)
            dimension "channel"
        }
    }
    splits {
        abi {
            enable true
            reset()
            include "x86", "x86_64"
            universalApk true
        }
    }
}

repositories {
    flatDir{
        dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
    implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
    implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
    implementation project(':capacitor-android')
    testImplementation "junit:junit:$junitVersion"
    androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
    androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
    implementation project(':capacitor-cordova-android-plugins')
}

apply from: 'capacitor.build.gradle'

try {
    def servicesJSON = file('google-services.json')
    if (servicesJSON.text) {
        apply plugin: 'com.google.gms.google-services'
    }
} catch(Exception e) {
    logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
}


================================================
FILE: android/app/capacitor.build.gradle
================================================
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN

android {
  compileOptions {
      sourceCompatibility JavaVersion.VERSION_17
      targetCompatibility JavaVersion.VERSION_17
  }
}

apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
    implementation project(':capacitor-mlkit-barcode-scanning')
    implementation project(':capacitor-app')
    implementation project(':capacitor-app-launcher')
    implementation project(':capacitor-clipboard')
    implementation project(':capacitor-filesystem')
    implementation project(':capacitor-haptics')
    implementation project(':capacitor-network')
    implementation project(':capacitor-share')
    implementation project(':capacitor-status-bar')
    implementation project(':capacitor-toast')
    implementation project(':capacitor-secure-storage-plugin')

}


if (hasProperty('postBuildExtras')) {
  postBuildExtras()
}


================================================
FILE: android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
================================================
package com.getcapacitor.myapp;

import static org.junit.Assert.*;

import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {

    @Test
    public void useAppContext() throws Exception {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();

        assertEquals("com.getcapacitor.app", appContext.getPackageName());
    }
}


================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:hardwareAccelerated="true"
        android:enableOnBackInvokedCallback="true"
        android:theme="@style/AppTheme">

        <activity
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
            android:name=".MainActivity"
            android:label="@string/title_activity_main"
            android:theme="@style/AppTheme.NoActionBarLaunch"
            android:launchMode="singleTask"
            android:exported="true">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </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="http" />
                <data android:scheme="https" />
                <data android:host="app.mutinywallet.com" />
            </intent-filter>

        </activity>

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"></meta-data>
        </provider>
    </application>

    <!-- Permissions -->

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
    <uses-feature android:name="android.hardware.camera" />

    <uses-permission android:name="android.permission.READ_CLIPBOARD" />
    <uses-permission android:name="android.permission.WRITE_CLIPBOARD" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>


================================================
FILE: android/app/src/main/java/com/mutinywallet/mutinywallet/MainActivity.java
================================================
package com.mutinywallet.mutinywallet;

import android.os.Bundle;
import android.webkit.WebView;

import com.getcapacitor.BridgeActivity;

public class MainActivity extends BridgeActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onStart() {
        super.onStart();
        WebView webview = getBridge().getWebView();
        webview.setOverScrollMode(WebView.OVER_SCROLL_NEVER);
    }
}


================================================
FILE: android/app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
    android:height="108dp"
    android:width="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#3DDC84"
          android:pathData="M0,0h108v108h-108z"/>
    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>


================================================
FILE: android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>


================================================
FILE: android/app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>


================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

================================================
FILE: android/app/src/main/res/values/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="ic_launcher_background">#FFFFFF</color>
</resources>

================================================
FILE: android/app/src/main/res/values/strings.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="app_name">Mutiny Wallet</string>
    <string name="title_activity_main">Mutiny Wallet</string>
    <string name="package_name">com.mutinywallet.mutinywallet</string>
    <string name="custom_url_scheme">mutiny</string>
</resources>


================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:background">@null</item>
    </style>


    <style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
        <item name="android:background">@drawable/splash</item>
    </style>
</resources>

================================================
FILE: android/app/src/main/res/xml/file_paths.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="." />
    <cache-path name="my_cache_images" path="." />
</paths>

================================================
FILE: android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java
================================================
package com.getcapacitor.myapp;

import static org.junit.Assert.*;

import org.junit.Test;

/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {

    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }
}


================================================
FILE: android/build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.2.2'
        classpath 'com.google.gms:google-services:4.4.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

apply from: "variables.gradle"

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: android/capacitor.settings.gradle
================================================
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/android/capacitor')

include ':capacitor-mlkit-barcode-scanning'
project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning/android')

include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app/android')

include ':capacitor-app-launcher'
project(':capacitor-app-launcher').projectDir = new File('../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher/android')

include ':capacitor-clipboard'
project(':capacitor-clipboard').projectDir = new File('../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard/android')

include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem/android')

include ':capacitor-haptics'
project(':capacitor-haptics').projectDir = new File('../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics/android')

include ':capacitor-network'
project(':capacitor-network').projectDir = new File('../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network/android')

include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share/android')

include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar/android')

include ':capacitor-toast'
project(':capacitor-toast').projectDir = new File('../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast/android')

include ':capacitor-secure-storage-plugin'
project(':capacitor-secure-storage-plugin').projectDir = new File('../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin/android')


================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: android/gradle.properties
================================================
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true


================================================
FILE: android/gradlew
================================================
#!/bin/sh

#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
#
#   Gradle start up script for POSIX generated by Gradle.
#
#   Important for running:
#
#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
#       noncompliant, but you have some other compliant shell such as ksh or
#       bash, then to run this script, type that shell name before the whole
#       command line, like:
#
#           ksh Gradle
#
#       Busybox and similar reduced shells will NOT work, because this script
#       requires all of these POSIX shell features:
#         * functions;
#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
#         * compound commands having a testable exit status, especially «case»;
#         * various built-in commands including «command», «set», and «ulimit».
#
#   Important for patching:
#
#   (2) This script targets any POSIX shell, so it avoids extensions provided
#       by Bash, Ksh, etc; in particular arrays are avoided.
#
#       The "traditional" practice of packing multiple parameters into a
#       space-separated string is a well documented source of bugs and security
#       problems, so this is (mostly) avoided, by progressively accumulating
#       options in "$@", and eventually passing that to Java.
#
#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
#       see the in-line comments for details.
#
#       There are tweaks for specific operating systems such as AIX, CygWin,
#       Darwin, MinGW, and NonStop.
#
#   (3) This script is generated from the Groovy template
#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
#       within the Gradle project.
#
#       You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]
do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

warn () {
    echo "$*"
} >&2

die () {
    echo
    echo "$*"
    echo
    exit 1
} >&2

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in                #(
  CYGWIN* )         cygwin=true  ;; #(
  Darwin* )         darwin=true  ;; #(
  MSYS* | MINGW* )  msys=true    ;; #(
  NONSTOP* )        nonstop=true ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD=$JAVA_HOME/jre/sh/java
    else
        JAVACMD=$JAVA_HOME/bin/java
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD=java
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
    case $MAX_FD in #(
      max*)
        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC3045
        MAX_FD=$( ulimit -H -n ) ||
            warn "Could not query maximum file descriptor limit"
    esac
    case $MAX_FD in  #(
      '' | soft) :;; #(
      *)
        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC3045
        ulimit -n "$MAX_FD" ||
            warn "Could not set maximum file descriptor limit to $MAX_FD"
    esac
fi

# Collect all arguments for the java command, stacking in reverse order:
#   * args from the command line
#   * the main class name
#   * -classpath
#   * -D...appname settings
#   * --module-path (only if needed)
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.

# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )

    JAVACMD=$( cygpath --unix "$JAVACMD" )

    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    for arg do
        if
            case $arg in                                #(
              -*)   false ;;                            # don't mess with options #(
              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
                    [ -e "$t" ] ;;                      #(
              *)    false ;;
            esac
        then
            arg=$( cygpath --path --ignore --mixed "$arg" )
        fi
        # Roll the args list around exactly as many times as the number of
        # args, so each arg winds up back in the position where it started, but
        # possibly modified.
        #
        # NB: a `for` loop captures its iteration list before it begins, so
        # changing the positional parameters here affects neither the number of
        # iterations, nor the values presented in `arg`.
        shift                   # remove old arg
        set -- "$@" "$arg"      # push replacement arg
    done
fi

# Collect all arguments for the java command;
#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
#     shell script including quotes and variable substitutions, so put them in
#     double quotes to make sure that they get re-expanded; and
#   * put everything else in single quotes, so that it's not re-expanded.

set -- \
        "-Dorg.gradle.appname=$APP_BASE_NAME" \
        -classpath "$CLASSPATH" \
        org.gradle.wrapper.GradleWrapperMain \
        "$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
    die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
#   set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#

eval "set -- $(
        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
        xargs -n1 |
        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
        tr '\n' ' '
    )" '"$@"'

exec "$JAVACMD" "$@"


================================================
FILE: android/gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: android/settings.gradle
================================================
include ':app'
include ':capacitor-cordova-android-plugins'
project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')

apply from: 'capacitor.settings.gradle'

================================================
FILE: android/variables.gradle
================================================
ext {
    minSdkVersion = 22
    compileSdkVersion = 34
    targetSdkVersion = 34
    androidxActivityVersion = '1.8.0'
    androidxAppCompatVersion = '1.6.1'
    androidxCoordinatorLayoutVersion = '1.2.0'
    androidxCoreVersion = '1.12.0'
    androidxFragmentVersion = '1.6.2'
    coreSplashScreenVersion = '1.0.1'
    androidxWebkitVersion = '1.9.0'
    junitVersion = '4.13.2'
    androidxJunitVersion = '1.1.5'
    androidxEspressoCoreVersion = '3.5.1'
    cordovaAndroidVersion = '10.1.1'
}

================================================
FILE: capacitor.config.ts
================================================
import { CapacitorConfig } from "@capacitor/cli";

const config: CapacitorConfig = {
    appId: "com.mutinywallet.mutinywallet",
    backgroundColor: "171717",
    appName: "Mutiny Wallet",
    webDir: "dist/public",
    server: {
        androidScheme: "https"
    }
};

export default config;


================================================
FILE: default.conf
================================================
server {
    listen 80;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}


================================================
FILE: e2e/encrypt.spec.ts
================================================
import { expect, test } from "@playwright/test";

import { loadHome, visitSettings } from "./utils";

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("test local encrypt", async ({ page }) => {
    await loadHome(page);
    await visitSettings(page);

    // Click the "Backup" link
    await page.click("text=Backup");

    // Click the "Tap to reveal seed words" button
    await page.click("text=Tap to reveal seed words");

    // Click all three checkboxes
    await page.getByLabel("I wrote down the words").click({ force: true });
    await page
        .getByLabel("I understand that my funds are my responsibility")
        .click({ force: true });
    await page
        .getByLabel("I'm not lying just to get this over with")
        .click({ force: true });

    // The "I wrote down the words" button should not be disabled
    const wroteDownButton = await page.locator("button", {
        hasText: "I wrote down the words"
    });
    await expect(wroteDownButton).not.toBeDisabled();

    // Click the "I wrote down the words" button
    await wroteDownButton.click();

    // Make sure the balance box ready light is on
    await page.locator("title=READY");

    // Go back to settings / change password
    await visitSettings(page);
    await page.click("text=Security");

    // The header should now say "Encrypt your seed words"
    await expect(page.locator("h1")).toContainText(["Encrypt your seed words"]);

    // Set a password
    // 1. Find the input field with the name "password"
    const passwordInput = await page.locator(`input[name='password']`);

    // 2. Type the password into the input field
    await passwordInput.fill("test");

    // 3. Find the input field with the name "confirmPassword"
    const confirmPasswordInput = await page.locator(
        `input[name='confirmPassword']`
    );

    // 4. Type the password into the input field
    await confirmPasswordInput.fill("test");

    // The "Encrypt" button should not be disabled
    const encryptButton = await page.locator("button", { hasText: "Encrypt" });
    await expect(encryptButton).toBeEnabled();

    // wait 5 seconds for no reason (SADLY THIS IS IMPORTANT FOR THE TEST TO PASS)
    await page.waitForTimeout(5000);

    // Click the "Encrypt" button
    await encryptButton.click();

    // Wait for a modal with the text "Enter your password"
    await page.getByText("Enter your password").waitFor();

    // Find the input field with the name "password"
    const passwordInput2 = await page.locator(`input[name='password']`);

    // Type the password into the input field
    await passwordInput2.fill("test");

    // Click the "Decrypt Wallet" button
    await page.click("text=Decrypt Wallet");

    // Should have a balance up top now
    await page.locator(`text=0 sats`).first().waitFor();

    const shutdownPopup = page.getByText("Mutiny Wallet is Shutting Down");
    if (await shutdownPopup.isVisible()) {
        // Click the close button
        await page.getByRole("button").nth(1).click();
    }
});


================================================
FILE: e2e/fedimint.spec.ts
================================================
import { expect, test } from "@playwright/test";

import { loadHome } from "./utils";

const SIGNET_INVITE_CODE =
    "fed11qgqzc2nhwden5te0vejkg6tdd9h8gepwvejkg6tdd9h8garhduhx6at5d9h8jmn9wshxxmmd9uqqzgxg6s3evnr6m9zdxr6hxkdkukexpcs3mn7mj3g5pc5dfh63l4tj6g9zk4er";

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("fedmint join, receive, send", async ({ page }) => {
    await loadHome(page);

    // Click the top left button (it's the profile button), a child of header
    // TODO: better ARIA stuff
    await page.locator(`header button`).first().click();

    // Click "Join a federation" cta
    await page.click("text=Join a federation");

    // Fill the input with the federation code
    await page.fill("input[name='federation_code']", SIGNET_INVITE_CODE);

    const addButton = await page.getByRole("button", { name: "Add" }).first();

    // Click the "Add" button
    await addButton.click();

    // Wait for a header to appear with the text "MutinySignetFederation"
    await page.getByText("MutinySignetFederation").waitFor();

    // Navigate back to profile
    await page.goBack();

    // Navigate back home
    await page.goBack();

    // Click the top left button (it's the profile button), a child of header
    // TODO: better ARIA stuff
    await page.locator(`header button`).first().click();

    // Make sure there's text that says "fedimint"
    await page.locator("text=fedimint").first();

    // Navigate back home
    await page.goBack();

    // Click the fab button
    await page.locator("#fab").click();
    // Click the receive button in the fab
    await page.locator("text=Receive").last().click();

    // Expect the url to conain receive
    await expect(page).toHaveURL(/.*receive/);

    // At least one h1 should show "0 sats"
    await expect(page.locator("h1")).toContainText(["0 SATS"]);

    // Type 100 into the input
    await page.locator("#sats-input").pressSequentially("100");

    // Now the h1 should show "100,000 sats"
    await expect(page.locator("h1")).toContainText(["100 SATS"]);

    // There should be a button with the text "Continue" and it should not be disabled
    const continueButton = await page.locator("button", {
        hasText: "Continue"
    });
    await expect(continueButton).not.toBeDisabled();

    await continueButton.click();

    await expect(
        page.getByText("Keep Mutiny open to complete the payment.")
    ).toBeVisible();

    // Locate an SVG inside a div with id "qr"
    const qrCode = await page.locator("#qr > svg");

    await expect(qrCode).toBeVisible();

    const value = await qrCode.getAttribute("value");

    // The SVG's value property includes "bitcoin:l"
    expect(value).toContain("lightning:l");

    const lightningInvoice = value?.split("lightning:")[1];

    // Post the lightning invoice to the server
    const _response = await fetch(
        "https://faucet.mutinynet.com/api/lightning",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                bolt11: lightningInvoice
            })
        }
    );

    // Wait for an h1 to appear in the dom that says "Payment Received"
    await page.waitForSelector("text=Payment Received");

    // Click the "Nice" button
    await page.click("text=Nice");

    // Make sure we have 100 sats in the top balance
    await page.waitForSelector("text=100 SATS");

    // Now we send
    await page.locator("#fab").click();
    await page.locator("text=Send").last().click();

    // type refund@lnurl-staging.mutinywallet.com
    const sendInput = await page.locator("input");
    await sendInput.fill("refund@lnurl-staging.mutinywallet.com");

    await page.click("text=Continue");

    // Wait for the destination to show up
    await page.waitForSelector("text=LIGHTNING");

    // Type 90 into the input
    await page.locator("#sats-input").fill("90");

    // Now the h1 should show "90 sats"
    await expect(page.locator("h1")).toContainText(["90 SATS"]);

    // There should be a button with the text "Confirm Send" and it should not be disabled
    const confirmButton = await page.locator("button", {
        hasText: "Confirm Send"
    });
    await expect(confirmButton).not.toBeDisabled();

    await confirmButton.click();

    // Wait for an h1 to appear in the dom that says "Payment Sent"
    await page.waitForSelector("text=Payment Sent");
});


================================================
FILE: e2e/load.spec.ts
================================================
import { test } from "@playwright/test";

import { loadHome } from "./utils";

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("initial load", async ({ page }) => {
    await loadHome(page);
});


================================================
FILE: e2e/restore.spec.ts
================================================
import { expect, test } from "@playwright/test";

import { visitSettings } from "./utils";

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("restore from seed @slow", async ({ page }) => {
    // Start on the home page
    await expect(page).toHaveTitle(/Mutiny Wallet/);
    await page.waitForSelector("text=Welcome to the Mutiny!");

    console.log("Waiting for new wallet to be created...");

    await page.locator(`button:has-text('Import Existing')`).click();

    // should have 100k sats on-chain
    const TEST_SEED_WORDS =
        "rival hood review write spoon tide orange ill opera enrich clip acoustic";

    // There should be some warning text: "This will replace your existing wallet"
    await expect(page.locator("p")).toContainText([
        "This will replace your existing wallet"
    ]);

    const seedWords = TEST_SEED_WORDS.split(" ");

    // Find the input field with the name "words.0"
    for (let i = 0; i < 12; i++) {
        const wordInput = await page.locator(`input[name='words.${i}']`);

        // Type the seed words into the input field
        await wordInput.fill(seedWords[i]);
    }

    // There should be a button with the text "Restore" and it should not be disabled
    const restoreButton = await page.locator("button", { hasText: "Restore" });
    await expect(restoreButton).not.toBeDisabled();

    restoreButton.click();

    // A modal should pop up, click the "Confirm" button
    const confirmButton = await page.locator("button", { hasText: "Confirm" });
    await confirmButton.click();

    // Eventually we should have a balance of 100k sats
    await page.locator("text=100,000 SATS");

    // Now we should clean up after ourselves and delete the wallet
    await visitSettings(page);

    // Click the "Restore" link
    await page.click("text=Admin Page");

    // Clicke the Delete Everything button
    await page.click("text=Delete Everything");

    // A modal should pop up, click the "Confirm" button
    const confirmDeleteButton = await page.locator("button", {
        hasText: "Confirm"
    });

    // wait 5 seconds for no reason
    await page.waitForTimeout(5000);

    await confirmDeleteButton.click();

    await page.locator("text=Welcome to the Mutiny!");
});


================================================
FILE: e2e/roundtrip.spec.ts
================================================
import { expect, test } from "@playwright/test";

import { loadHome, visitSettings } from "./utils";

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("rountrip receive and send", async ({ page }) => {
    test.slow(); // tell playwright that this test is slow

    await loadHome(page);

    await page.locator("#fab").click();
    await page.locator("text=Receive").last().click();

    // Expect the url to contain receive
    await expect(page).toHaveURL(/.*receive/);

    // At least one h1 should show "0 sats"
    await expect(page.locator("h1")).toContainText(["0 SATS"]);

    // At least one h2 should show "0 USD"
    // await expect(page.locator("h2")).toContainText(["$0 USD"]);
    await page.waitForSelector("text=$0 USD");

    // Type 100000 into the input
    await page.locator("#sats-input").pressSequentially("100000");

    // Now the h1 should show "100,000 sats"
    await expect(page.locator("h1")).toContainText(["100,000 SATS"]);

    // There should be a button with the text "Continue" and it should not be disabled
    const continueButton = await page.locator("button", {
        hasText: "Continue"
    });
    await expect(continueButton).not.toBeDisabled();

    // Wait one second
    // TODO: figure out how to not get an error without waiting
    await page.waitForTimeout(1000);

    continueButton.click();

    await expect(
        page.getByText("Keep Mutiny open to complete the payment.")
    ).toBeVisible();

    // Locate an SVG inside a div with id "qr"
    const qrCode = await page.locator("#qr > svg");

    await expect(qrCode).toBeVisible();

    const value = await qrCode.getAttribute("value");

    // The SVG's value property includes "lightning:l"
    expect(value).toContain("lightning:l");

    // Post the lightning invoice to the server
    const response = await fetch("https://faucet.mutinynet.com/api/lightning", {
        method: "POST",
        mode: "cors",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            bolt11: value
        })
    });

    if (!response.ok) {
        response.text().then((text) => {
            throw new Error("failed to post invoice to faucet: " + text);
        });
    }

    // Wait for an h1 to appear in the dom that says "Payment Received"
    await page.waitForSelector("text=Payment Received", { timeout: 30000 });

    // Click the "Nice" button
    await page.click("text=Nice");

    // Now we send
    await page.locator("#fab").click();
    await page.locator("text=Send").click();

    // In the textarea with the placeholder "bitcoin:..." type refund@lnurl-staging.mutinywallet.com
    const sendInput = await page.locator("input");
    await sendInput.fill("refund@lnurl-staging.mutinywallet.com");

    await page.click("text=Continue");

    // Wait two seconds (the destination doesn't show up immediately)
    // TODO: figure out how to not get an error without waiting
    await page.waitForTimeout(2000);

    // Type 10000 into the input
    await page.locator("#sats-input").fill("10000");

    // Now the h1 should show "100,000 sats"
    await expect(page.locator("h1")).toContainText(["10,000 SATS"]);

    // There should be a button with the text "Confirm Send" and it should not be disabled
    const confirmButton = await page.locator("button", {
        hasText: "Confirm Send"
    });
    await expect(confirmButton).not.toBeDisabled();

    confirmButton.click();

    // Wait for an h1 to appear in the dom that says "Payment Sent"
    await page.waitForSelector("text=Payment Sent", { timeout: 30000 });

    // Click the "Nice" button to go home
    await page.click("text=Nice");

    // Click settings
    await visitSettings(page);

    // Click "lightning channels"
    await page.click("text=Lightning Channels");

    // Close the channel
    await page.getByText("You have 1 lightning channel.").waitFor();

    await page.click("text=Online Channels");

    // Idk why the node isn't ready to close channels right away
    await page.waitForTimeout(5000);

    await page.click("text=Close");

    await page.click("text=Confirm");

    // wait for the channel to close
    await page.waitForTimeout(5000);

    await page
        .getByText(
            "It looks like you don't have any channels yet. To get started, receive some sats over lightning, or swap some on-chain funds into a channel. Get your hands dirty!"
        )
        .waitFor();
});


================================================
FILE: e2e/routes.spec.ts
================================================
import { expect, Page, test } from "@playwright/test";

import { loadHome, visitSettings } from "./utils";

const routes = [
    "/",
    "/feedback",
    "/receive",
    "/scanner",
    "/search",
    "/send",
    "/settings"
];

const settingsRoutes = [
    "/admin",
    "/backup",
    "/channels",
    "/connections",
    "/currency",
    "/emergencykit",
    "/restore",
    "/servers",
    "/nostrkeys"
];

const settingsRoutesPrefixed = settingsRoutes.map((route) => {
    return "/settings" + route;
});

const allRoutes = routes.concat(settingsRoutesPrefixed);

// Create a JS Map of all routes so we can check them off one by one
const checklist = new Map();
allRoutes.forEach((route) => {
    checklist.set(route, false);
});

// Only works if there's a link to the route on the page
async function checkRoute(
    page: Page,
    route: string,
    expectedHeader: string,
    checklist: Map<string, boolean>
) {
    await page.locator(`a[href='${route}']`).first().click();
    await expect(page.locator("h1").first()).toHaveText(expectedHeader);
    checklist.set(route, true);
}

test.beforeEach(async ({ page }) => {
    await page.goto("http://localhost:3420/");
});

test("visit each route", async ({ page }) => {
    await loadHome(page);

    checklist.set("/", true);

    await visitSettings(page);

    checklist.set("/settings", true);

    // Lightning Channels
    await checkRoute(
        page,
        "/settings/channels",
        "Lightning Channels",
        checklist
    );
    await page.goBack();

    // Backup
    await checkRoute(page, "/settings/backup", "Backup", checklist);
    await page.goBack();

    // Restore
    await checkRoute(page, "/settings/restore", "Restore", checklist);
    await page.goBack();

    // Currency
    await checkRoute(page, "/settings/currency", "Currency", checklist);
    await page.goBack();

    // Servers
    await checkRoute(page, "/settings/servers", "Servers", checklist);
    await page.goBack();

    // Nostr Keys
    await checkRoute(page, "/settings/nostrkeys", "Nostr Keys", checklist);
    await page.goBack();

    // Emergency Kit
    await checkRoute(
        page,
        "/settings/emergencykit",
        "Emergency Kit",
        checklist
    );
    await page.goBack();

    // Admin
    await checkRoute(page, "/settings/admin", "Secret Debug Tools", checklist);
    await page.goBack();

    // Feedback
    await checkRoute(page, "/feedback", "Give us feedback!", checklist);
    await page.goBack();

    // Go back home
    await page.goBack();

    // Try the fab button
    await page.locator("#fab").click();
    await page.locator("text=Send").click();
    await expect(page.locator("input").first()).toBeFocused();

    // Send is covered in another test
    checklist.set("/send", true);

    await page.goBack();

    // Try the fab button again
    await page.locator("#fab").click();
    // (There are actually two buttons with the "Receive text on first run)
    await page.locator("text=Receive").last().click();

    await expect(page.locator("h1").first()).toHaveText("Receive Bitcoin");

    // Actual receive is covered in another test
    checklist.set("/receive", true);

    await page.goBack();

    // Try the fab button again
    await page.locator("#fab").click();
    await page.locator("text=Scan").click();

    // Scanner
    await expect(
        page.locator("button:has-text('Paste Something')")
    ).toBeVisible();
    checklist.set("/scanner", true);

    // Visit connections nwa params
    const nwaParams =
        "/settings/connections?nwa=nostr%2Bwalletauth%3A%2F%2Fe552dec5821ef94dc1b9138a347b4b1d8dcb595e31f5c89352e50dc11255e0f4%3Frelay%3Dwss%253A%252F%252Frelay.damus.io%252F%26secret%3D0bfe616c5e126a7c%26required_commands%3Dpay_invoice%26budget%3D21%252Fday%26identity%3D32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245";
    await page.goto("http://localhost:3420" + nwaParams);
    await expect(page.locator('[role="dialog"] h2 header').first()).toHaveText(
        "Add Connection"
    );

    // print how many routes we've visited
    checklist.forEach((value, key) => {
        console.log(`${key}: ${value}`);
    });
});


================================================
FILE: e2e/utils.ts
================================================
import { expect, Page } from "@playwright/test";

export async function loadHome(page: Page) {
    // Start on the home page
    await expect(page).toHaveTitle(/Mutiny Wallet/);
    await page.waitForSelector("text=Welcome to the Mutiny!");

    console.log("Waiting for new wallet to be created...");

    await page.locator(`button:has-text('New Wallet')`).click();

    await page.locator("text=Create your profile").first();

    await page.locator("button:has-text('Skip for now')").click();

    // await page.getByText("Pick a Federation").waitFor();

    // await page.locator("button:has-text('Skip for now')").click();

    // await page.locator(`button:has-text('Confirm')`).click();

    // Should have a balance up top now
    await page.locator(`text=0 sats`).first().waitFor();

    const shutdownPopup = page.getByText("Mutiny Wallet is Shutting Down");
    if (await shutdownPopup.isVisible()) {
        // Click the close button
        await page.getByRole("button").nth(1).click();
    }
}

export async function visitSettings(page: Page) {
    // Find an image with an alt text of "mutiny" and click it
    // TODO: probably should have better ARIA stuff for this
    await page.locator("img[alt='mutiny']").first().click();
    await expect(page.locator("h1").first()).toHaveText("Settings");
}


================================================
FILE: flake.nix
================================================
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };

      in
      {
        devShells.default = pkgs.mkShell {
          buildInputs = [
            pkgs.corepack_20
            pkgs.nodejs_20
            pkgs.python3
            pkgs.just
          ];
          shellHook = ''
            corepack prepare pnpm@8.15.5 --activate
          '';
        };
      });
}


================================================
FILE: index.html
================================================
<!doctype html>
<html>
    <head>
        <title data-sm="">Mutiny Wallet</title>
        <meta charset="utf-8" />
        <meta
            name="viewport"
            content="width=device-width, initial-scale=1.0 height=device-height viewport-fit=cover user-scalable=no"
        />
        <link rel="manifest" href="/manifest.webmanifest" />
        <meta name="theme-color" content="#171717" />
        <meta
            name="description"
            content="Mutiny is a self-custodial lightning wallet that runs in the browser."
        />
        <link rel="icon" href="/favicon.ico" />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content="Mutiny Wallet" />
        <meta
            name="twitter:description"
            content="Mutiny is a self-custodial lightning wallet that runs in the browser."
        />
        <meta name="twitter:site" content="https://app.mutinywallet.com/" />
        <meta
            name="twitter:image"
            content="https://app.mutinywallet.com/images/twitter_card_image.png"
        />
        <meta property="og:type" content="website" />
        <meta property="og:title" content="Mutiny Wallet" />
        <meta
            property="og:description"
            content="Mutiny is a self-custodial lightning wallet that runs in the browser."
        />
        <meta property="og:url" content="https://app.mutinywallet.com/" />
        <meta
            property="og:image"
            content="https://app.mutinywallet.com/images/twitter_card_image.png"
        />
        <link rel="apple-touch-icon" href="/images/icon.png" sizes="512x512" />
        <link rel="mask-icon" href="/mutiny_logo_mask.svg" color="#000" />
        <style>
            #no-wasm {
                display: none;
            }

            #no-wasm,
            #no-script {
                margin: 1rem;
            }
            body {
                background-color: hsla(0, 0%, 5%, 1);
            }
        </style>
    </head>
    <body>
        <div id="no-wasm">
            <p>
                <img
                    src="/mutiny-pixel-logo.png"
                    width="75px"
                    alt="Mutiny Wallet"
                />
            </p>
            <p>
                Your browser does not support WebAssembly, or it's disabled.
                Please update or enable WebAssembly to run this app.
            </p>
            <p>
                If you're running iOS in lockdown mode you'll need to
                <a href="https://support.apple.com/en-us/HT212650"
                    >add an exception for Mutiny Wallet.</a
                >
            </p>
        </div>
        <noscript>
            <div id="no-script">
                <p>
                    <img
                        src="/mutiny-pixel-logo.png"
                        width="75px"
                        alt="Mutiny Wallet"
                    />
                </p>
                <p>
                    Mutiny is a self-custodial wallet that runs in the browser.
                    JavaScript must be enabled.
                </p>
            </div>
        </noscript>
        <div id="root"></div>
        <script type="module" src="/src/index.tsx"></script>
        <script>
            // Check for WebAssembly support
            if (
                typeof WebAssembly === "object" &&
                typeof WebAssembly.instantiate === "function"
            ) {
                var module = new WebAssembly.Module(
                    new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0])
                ); // This is an empty wasm module
                if (
                    WebAssembly.Module instanceof Function &&
                    module instanceof WebAssembly.Module
                )
                    console.log("WebAssembly is supported");
                else document.getElementById("no-wasm").style.display = "block";
            } else {
                document.getElementById("no-wasm").style.display = "block";
            }
        </script>
    </body>
</html>


================================================
FILE: ios/.gitignore
================================================
App/build
App/Pods
App/output
App/App/public
DerivedData
xcuserdata

# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins

# Generated Config files
App/App/capacitor.config.json
App/App/config.xml

App/App.app.dSYM.zip
App/App.ipa

================================================
FILE: ios/App/App/App.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.associated-domains</key>
	<array>
		<string>applinks:app.mutinywallet.com</string>
	</array>
</dict>
</plist>


================================================
FILE: ios/App/App/AppDelegate.swift
================================================
import UIKit
import Capacitor

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        if #available(iOS 16.4, *) {
            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                if let vc = self.window?.rootViewController as? CAPBridgeViewController {
                    vc.bridgedWebView?.isInspectable = true;
                }
            }
        }
        
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        // Called when the app was launched with a url. Feel free to add additional processing here,
        // but if you want the App API to support tracking app url opens, make sure to keep this call
        return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
    }

    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        // Called when the app was launched with an activity, including Universal Links.
        // Feel free to add additional processing here, but if you want the App API to support
        // tracking app url opens, make sure to keep this call
        return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
    }

}


================================================
FILE: ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images": [
    {
      "filename": "AppIcon-512@2x.png",
      "idiom": "universal",
      "platform": "ios",
      "size": "1024x1024"
    },
    {
      "idiom": "ipad",
      "size": "20x20",
      "scale": "1x",
      "filename": "AppIcon-20x20@1x.png"
    },
    {
      "idiom": "iphone",
      "size": "20x20",
      "scale": "2x",
      "filename": "AppIcon-20x20@2x.png"
    },
    {
      "idiom": "ipad",
      "size": "20x20",
      "scale": "2x",
      "filename": "AppIcon-20x20@2x-1.png"
    },
    {
      "idiom": "iphone",
      "size": "20x20",
      "scale": "3x",
      "filename": "AppIcon-20x20@3x.png"
    },
    {
      "idiom": "ipad",
      "size": "29x29",
      "scale": "1x",
      "filename": "AppIcon-29x29@1x.png"
    },
    {
      "idiom": "iphone",
      "size": "29x29",
      "scale": "2x",
      "filename": "AppIcon-29x29@2x.png"
    },
    {
      "idiom": "ipad",
      "size": "29x29",
      "scale": "2x",
      "filename": "AppIcon-29x29@2x-1.png"
    },
    {
      "idiom": "iphone",
      "size": "29x29",
      "scale": "3x",
      "filename": "AppIcon-29x29@3x.png"
    },
    {
      "idiom": "ipad",
      "size": "40x40",
      "scale": "1x",
      "filename": "AppIcon-40x40@1x.png"
    },
    {
      "idiom": "iphone",
      "size": "40x40",
      "scale": "2x",
      "filename": "AppIcon-40x40@2x.png"
    },
    {
      "idiom": "ipad",
      "size": "40x40",
      "scale": "2x",
      "filename": "AppIcon-40x40@2x-1.png"
    },
    {
      "idiom": "iphone",
      "size": "40x40",
      "scale": "3x",
      "filename": "AppIcon-40x40@3x.png"
    },
    {
      "idiom": "iphone",
      "size": "60x60",
      "scale": "2x",
      "filename": "AppIcon-60x60@2x.png"
    },
    {
      "idiom": "iphone",
      "size": "60x60",
      "scale": "3x",
      "filename": "AppIcon-60x60@3x.png"
    },
    {
      "idiom": "ipad",
      "size": "76x76",
      "scale": "1x",
      "filename": "AppIcon-76x76@1x.png"
    },
    {
      "idiom": "ipad",
      "size": "76x76",
      "scale": "2x",
      "filename": "AppIcon-76x76@2x.png"
    },
    {
      "idiom": "ipad",
      "size": "83.5x83.5",
      "scale": "2x",
      "filename": "AppIcon-83.5x83.5@2x.png"
    },
    {
      "idiom": "ios-marketing",
      "size": "1024x1024",
      "scale": "1x",
      "filename": "AppIcon-512@2x.png"
    }
  ],
  "info": {
    "author": "xcode",
    "version": 1
  }
}

================================================
FILE: ios/App/App/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: ios/App/App/Assets.xcassets/Splash.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "Default@1x~universal~anyany.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "filename" : "Default@1x~universal~anyany-dark.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "Default@2x~universal~anyany.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "filename" : "Default@2x~universal~anyany-dark.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "Default@3x~universal~anyany.png",
      "idiom" : "universal",
      "scale" : "3x"
    },
    {
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "filename" : "Default@3x~universal~anyany-dark.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: ios/App/App/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                    </imageView>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
    <resources>
        <image name="Splash" width="1366" height="1366"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>


================================================
FILE: ios/App/App/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
    </dependencies>
    <scenes>
        <!--My Custom View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="MyCustomViewController" customModule="App" customModuleProvider="target" sceneMemberID="viewController"/>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-41" y="-69"/>
        </scene>
    </scenes>
</document>


================================================
FILE: ios/App/App/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>Mutiny Wallet</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(MARKETING_VERSION)</string>
	<key>CFBundleVersion</key>
	<string>13</string>
	<key>ITSAppUsesNonExemptEncryption</key>
	<false/>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>NSCameraUsageDescription</key>
	<string>Mutiny uses the camera to scan QR codes of bitcoin addresses and lightning invoices.</string>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UIStatusBarStyle</key>
	<string>UIStatusBarStyleDarkContent</string>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
	</array>
	<key>UIViewControllerBasedStatusBarAppearance</key>
	<true/>
	<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>nostr+walletconnect</string>
	</array>
</dict>
</plist>


================================================
FILE: ios/App/App.ipa
================================================
[File too large to display: 17.7 MB]

================================================
FILE: ios/App/App.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 48;
	objects = {

/* Begin PBXBuildFile section */
		2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
		50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
		504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
		504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
		504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
		504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
		50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
		6E62BA8F2AA9491700710026 /* MyCustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E62BA8E2AA9491700710026 /* MyCustomViewController.swift */; };
		A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
		2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
		50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
		504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
		504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
		6E62BA8E2AA9491700710026 /* MyCustomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCustomViewController.swift; sourceTree = "<group>"; };
		6EE2E6442B02B59700B1E443 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
		AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
		FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		504EC3011FED79650016851F /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		504EC2FB1FED79650016851F = {
			isa = PBXGroup;
			children = (
				6E62BA8E2AA9491700710026 /* MyCustomViewController.swift */,
				504EC3061FED79650016851F /* App */,
				504EC3051FED79650016851F /* Products */,
				7F8756D8B27F46E3366F6CEA /* Pods */,
				27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		504EC3051FED79650016851F /* Products */ = {
			isa = PBXGroup;
			children = (
				504EC3041FED79650016851F /* App.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		504EC3061FED79650016851F /* App */ = {
			isa = PBXGroup;
			children = (
				6EE2E6442B02B59700B1E443 /* App.entitlements */,
				50379B222058CBB4000EE86E /* capacitor.config.json */,
				504EC3071FED79650016851F /* AppDelegate.swift */,
				504EC30B1FED79650016851F /* Main.storyboard */,
				504EC30E1FED79650016851F /* Assets.xcassets */,
				504EC3101FED79650016851F /* LaunchScreen.storyboard */,
				504EC3131FED79650016851F /* Info.plist */,
				2FAD9762203C412B000D30F8 /* config.xml */,
				50B271D01FEDC1A000F3C39B /* public */,
			);
			path = App;
			sourceTree = "<group>";
		};
		7F8756D8B27F46E3366F6CEA /* Pods */ = {
			isa = PBXGroup;
			children = (
				FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
				AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
			);
			name = Pods;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		504EC3031FED79650016851F /* App */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
			buildPhases = (
				6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
				504EC3001FED79650016851F /* Sources */,
				504EC3011FED79650016851F /* Frameworks */,
				504EC3021FED79650016851F /* Resources */,
				9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = App;
			productName = App;
			productReference = 504EC3041FED79650016851F /* App.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		504EC2FC1FED79650016851F /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastSwiftUpdateCheck = 0920;
				LastUpgradeCheck = 0920;
				TargetAttributes = {
					504EC3031FED79650016851F = {
						CreatedOnToolsVersion = 9.2;
						LastSwiftMigration = 1100;
						ProvisioningStyle = Automatic;
					};
				};
			};
			buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
			compatibilityVersion = "Xcode 8.0";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 504EC2FB1FED79650016851F;
			productRefGroup = 504EC3051FED79650016851F /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				504EC3031FED79650016851F /* App */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		504EC3021FED79650016851F /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
				50B271D11FEDC1A000F3C39B /* public in Resources */,
				504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
				50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
				504EC30D1FED79650016851F /* Main.storyboard in Resources */,
				2FAD9763203C412B000D30F8 /* config.xml in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
				"${PODS_ROOT}/Manifest.lock",
			);
			name = "[CP] Check Pods Manifest.lock";
			outputPaths = (
				"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
			showEnvVarsInLog = 0;
		};
		9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "[CP] Embed Pods Frameworks";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
			showEnvVarsInLog = 0;
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		504EC3001FED79650016851F /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
				6E62BA8F2AA9491700710026 /* MyCustomViewController.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXVariantGroup section */
		504EC30B1FED79650016851F /* Main.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				504EC30C1FED79650016851F /* Base */,
			);
			name = Main.storyboard;
			sourceTree = "<group>";
		};
		504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				504EC3111FED79650016851F /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		504EC3141FED79650016851F /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				CODE_SIGN_IDENTITY = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		504EC3151FED79650016851F /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				CODE_SIGN_IDENTITY = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		504EC3171FED79650016851F /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
				CODE_SIGN_IDENTITY = "Apple Development";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1434;
				DEVELOPMENT_TEAM = X773Y823TN;
				INFOPLIST_FILE = App/Info.plist;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				MARKETING_VERSION = 1.8.0;
				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
				PRODUCT_BUNDLE_IDENTIFIER = "com.mutinywallet.mutiny-test";
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		504EC3181FED79650016851F /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
				CODE_SIGN_IDENTITY = "Apple Development";
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
				CODE_SIGN_STYLE = Manual;
				CURRENT_PROJECT_VERSION = 1434;
				DEVELOPMENT_TEAM = "";
				"DEVELOPMENT_TEAM[sdk=iphoneos*]" = X773Y823TN;
				INFOPLIST_FILE = App/Info.plist;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				MARKETING_VERSION = 1.8.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.mutinywallet.mutiny;
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE_SPECIFIER = "";
				"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.mutinywallet.mutiny";
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				504EC3141FED79650016851F /* Debug */,
				504EC3151FED79650016851F /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				504EC3171FED79650016851F /* Debug */,
				504EC3181FED79650016851F /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 504EC2FC1FED79650016851F /* Project object */;
}


================================================
FILE: ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:App.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1530"
   version = "1.7">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES"
      buildArchitectures = "Automatic">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "504EC3031FED79650016851F"
               BuildableName = "App.app"
               BlueprintName = "App"
               ReferencedContainer = "container:App.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES"
      shouldAutocreateTestPlan = "YES">
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "504EC3031FED79650016851F"
            BuildableName = "App.app"
            BlueprintName = "App"
            ReferencedContainer = "container:App.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
      <CommandLineArguments>
         <CommandLineArgument
            argument = ""
            isEnabled = "YES">
         </CommandLineArgument>
      </CommandLineArguments>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "504EC3031FED79650016851F"
            BuildableName = "App.app"
            BlueprintName = "App"
            ReferencedContainer = "container:App.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: ios/App/App.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:App.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: ios/App/Gemfile
================================================
source "https://rubygems.org"

gem "fastlane"


================================================
FILE: ios/App/MyCustomViewController.swift
================================================
//
//  MyCustomViewController.swift
//  App
//
//  Created by Paul Miller on 9/6/23.
//

import UIKit
import Capacitor

class MyCustomViewController: CAPBridgeViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        webView!.allowsBackForwardNavigationGestures = true
        webView!.scrollView.bounces = false
        webView!.scrollView.alwaysBounceVertical = false
        webView!.scrollView.showsVerticalScrollIndicator = false
    }
}


================================================
FILE: ios/App/Podfile
================================================
require_relative '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios/scripts/pods_helpers'

platform :ios, '13.0'
use_frameworks!

# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true

def capacitor_pods
  pod 'Capacitor', :path => '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios'
  pod 'CapacitorCordova', :path => '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios'
  pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/.pnpm/@capacitor-mlkit+barcode-scanning@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor-mlkit/barcode-scanning'
  pod 'CapacitorApp', :path => '../../node_modules/.pnpm/@capacitor+app@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app'
  pod 'CapacitorAppLauncher', :path => '../../node_modules/.pnpm/@capacitor+app-launcher@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/app-launcher'
  pod 'CapacitorClipboard', :path => '../../node_modules/.pnpm/@capacitor+clipboard@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/clipboard'
  pod 'CapacitorFilesystem', :path => '../../node_modules/.pnpm/@capacitor+filesystem@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/filesystem'
  pod 'CapacitorHaptics', :path => '../../node_modules/.pnpm/@capacitor+haptics@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/haptics'
  pod 'CapacitorNetwork', :path => '../../node_modules/.pnpm/@capacitor+network@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/network'
  pod 'CapacitorShare', :path => '../../node_modules/.pnpm/@capacitor+share@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/share'
  pod 'CapacitorStatusBar', :path => '../../node_modules/.pnpm/@capacitor+status-bar@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/status-bar'
  pod 'CapacitorToast', :path => '../../node_modules/.pnpm/@capacitor+toast@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/toast'
  pod 'CapacitorSecureStoragePlugin', :path => '../../node_modules/.pnpm/capacitor-secure-storage-plugin@0.9.0_@capacitor+core@6.0.0/node_modules/capacitor-secure-storage-plugin'
end

target 'App' do
  capacitor_pods
  # Add your Pods here
end

post_install do |installer|
  assertDeploymentTarget(installer)
end


================================================
FILE: ios/App/fastlane/.gitignore
================================================
report.xml

================================================
FILE: ios/App/fastlane/Appfile
================================================
app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"]) # The bundle identifier of your app
apple_id(ENV["FASTLANE_APPLE_ID"]) # Your Apple Developer Portal username
itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"]) # App Store Connect Team ID
team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"]) # Developer Portal Team ID

# For more information about the Appfile, see:
#     https://docs.fastlane.tools/advanced/#appfile


================================================
FILE: ios/App/fastlane/Fastfile
================================================
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:ios)

ISSUER_ID = ENV["APPLE_ISSUER_ID"]
KEY_ID = ENV["APPLE_KEY_ID"]
KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]

KEYCHAIN_NAME = ENV["KEYCHAIN_NAME"]
KEYCHAIN_PASSWORD = ENV["KEYCHAIN_PASSWORD"]
APP_INDENTIFIER = ENV["DEVELOPER_APP_INDENTIFIER"]

def delete_temp_keychain(name)
  delete_keychain(
    name: name
  ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end

def create_temp_keychain(name, password)
  create_keychain(
    name: name,
    password: password,
    unlock: false,
    timeout: 0
  )
end

def ensure_temp_keychain(name, password)
  delete_temp_keychain(name)
  create_temp_keychain(name, password)
end

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    api_key = app_store_connect_api_key(
      key_id: KEY_ID,
      issuer_id: ISSUER_ID,
      key_content: KEY_CONTENT
    )

    # In CI we don't want to be prompted for a password
    ensure_temp_keychain(KEYCHAIN_NAME, KEYCHAIN_PASSWORD)

    match(
      type: "appstore",
      keychain_name: KEYCHAIN_NAME,
      keychain_password: KEYCHAIN_PASSWORD,
      readonly: is_ci
    )

    testflight_build_num = app_store_build_number(
      live: false,
      api_key: api_key,
      app_identifier: APP_INDENTIFIER
    )

    increment_build_number(
      build_number: testflight_build_num + 1,
      xcodeproj: "App.xcodeproj"
    )

    build_app(
      workspace: "App.xcworkspace",
      scheme: "App",
      xcargs: "-allowProvisioningUpdates"
    )
    upload_to_testflight(api_key: api_key, skip_waiting_for_build_processing: true)
  end

  lane :build do
    api_key = app_store_connect_api_key(
      key_id: KEY_ID,
      issuer_id: ISSUER_ID,
      key_content: KEY_CONTENT
    )

    # In CI we don't want to be prompted for a password
    ensure_temp_keychain(KEYCHAIN_NAME, KEYCHAIN_PASSWORD)

    match(
      type: "appstore",
      keychain_name: KEYCHAIN_NAME,
      keychain_password: KEYCHAIN_PASSWORD,
      readonly: is_ci
    )

    build_app(
      workspace: "App.xcworkspace",
      scheme: "App",
      xcargs: "-allowProvisioningUpdates"
    )
  end
end


================================================
FILE: ios/App/fastlane/Matchfile
================================================
CERTIFICATE_STORE_URL = ENV["CERTIFICATE_STORE_URL"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
FASTLANE_APPLE_ID = ENV["FASTLANE_APPLE_ID"]

git_url(CERTIFICATE_STORE_URL)

storage_mode("git")
type("appstore") # The default type, can be: appstore, adhoc, enterprise or development

git_basic_authorization(Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"))
username(FASTLANE_APPLE_ID)

# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
# username("user@fastlane.tools") # Your Apple Developer Portal username

# For all available options run `fastlane match --help`
# Remove the # in the beginning of the line to enable the other options

# The docs are available on https://docs.fastlane.tools/actions/match


================================================
FILE: ios/App/fastlane/README.md
================================================
fastlane documentation
----

# Installation

Make sure you have the latest version of the Xcode command line tools installed:

```sh
xcode-select --install
```

For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)

# Available Actions

## iOS

### ios beta

```sh
[bundle exec] fastlane ios beta
```

Push a new beta build to TestFlight

### ios build

```sh
[bundle exec] fastlane ios build
```



----

This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.

More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).

The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).


================================================
FILE: justfile
================================================
set dotenv-load := false

dev:
    pnpm run dev

pre:
    pnpm run pre-commit

local:
    pnpm install && pnpm link --global "@mutinywallet/mutiny-wasm"

remote:
    pnpm unlink --filter "@mutinywallet/mutiny-wasm" && pnpm install

native:
    pnpm install && pnpm build && npx cap sync

test:
    pnpm exec playwright test
    
test-ui:
    pnpm exec playwright test --ui

mainnet:
    cp .env.mainnet .env.local

signet:
    cp .env.signet .env.local


================================================
FILE: manifest.ts
================================================
import { ManifestOptions } from "vite-plugin-pwa";

const manifest: Partial<ManifestOptions> = {
    name: "Mutiny Wallet",
    orientation: "portrait",
    short_name: "Mutiny",
    description: "A lightning wallet",
    theme_color: "#000",
    display: "standalone",
    categories: ["finance", "social"],
    icons: [
        {
            src: "192.png",
            sizes: "192x192",
            type: "image/png"
        },
        {
            src: "512.png",
            sizes: "512x512",
            type: "image/png"
        },
        {
            src: "maskable_icon.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "maskable"
        },
        {
            src: "maskable_icon.png",
            sizes: "512x512",
            type: "image/png",
            purpose: "any"
        },
        {
            src: "windows11/SmallTile.scale-100.png",
            sizes: "71x71"
        },
        {
            src: "windows11/SmallTile.scale-125.png",
            sizes: "89x89"
        },
        {
            src: "windows11/SmallTile.scale-150.png",
            sizes: "107x107"
        },
        {
            src: "windows11/SmallTile.scale-200.png",
            sizes: "142x142"
        },
        {
            src: "windows11/SmallTile.scale-400.png",
            sizes: "284x284"
        },
        {
            src: "windows11/Square150x150Logo.scale-100.png",
            sizes: "150x150"
        },
        {
            src: "windows11/Square150x150Logo.scale-125.png",
            sizes: "188x188"
        },
        {
            src: "windows11/Square150x150Logo.scale-150.png",
            sizes: "225x225"
        },
        {
            src: "windows11/Square150x150Logo.scale-200.png",
            sizes: "300x300"
        },
        {
            src: "windows11/Square150x150Logo.scale-400.png",
            sizes: "600x600"
        },
        {
            src: "windows11/Wide310x150Logo.scale-100.png",
            sizes: "310x150"
        },
        {
            src: "windows11/Wide310x150Logo.scale-125.png",
            sizes: "388x188"
        },
        {
            src: "windows11/Wide310x150Logo.scale-150.png",
            sizes: "465x225"
        },
        {
            src: "windows11/Wide310x150Logo.scale-200.png",
            sizes: "620x300"
        },
        {
            src: "windows11/Wide310x150Logo.scale-400.png",
            sizes: "1240x600"
        },
        {
            src: "windows11/LargeTile.scale-100.png",
            sizes: "310x310"
        },
        {
            src: "windows11/LargeTile.scale-125.png",
            sizes: "388x388"
        },
        {
            src: "windows11/LargeTile.scale-150.png",
            sizes: "465x465"
        },
        {
            src: "windows11/LargeTile.scale-200.png",
            sizes: "620x620"
        },
        {
            src: "windows11/LargeTile.scale-400.png",
            sizes: "1240x1240"
        },
        {
            src: "windows11/Square44x44Logo.scale-100.png",
            sizes: "44x44"
        },
        {
            src: "windows11/Square44x44Logo.scale-125.png",
            sizes: "55x55"
        },
        {
            src: "windows11/Square44x44Logo.scale-150.png",
            sizes: "66x66"
        },
        {
            src: "windows11/Square44x44Logo.scale-200.png",
            sizes: "88x88"
        },
        {
            src: "windows11/Square44x44Logo.scale-400.png",
            sizes: "176x176"
        },
        {
            src: "windows11/StoreLogo.scale-100.png",
            sizes: "50x50"
        },
        {
            src: "windows11/StoreLogo.scale-125.png",
            sizes: "63x63"
        },
        {
            src: "windows11/StoreLogo.scale-150.png",
            sizes: "75x75"
        },
        {
            src: "windows11/StoreLogo.scale-200.png",
            sizes: "100x100"
        },
        {
            src: "windows11/StoreLogo.scale-400.png",
            sizes: "200x200"
        },
        {
            src: "windows11/SplashScreen.scale-100.png",
            sizes: "620x300"
        },
        {
            src: "windows11/SplashScreen.scale-125.png",
            sizes: "775x375"
        },
        {
            src: "windows11/SplashScreen.scale-150.png",
            sizes: "930x450"
        },
        {
            src: "windows11/SplashScreen.scale-200.png",
            sizes: "1240x600"
        },
        {
            src: "windows11/SplashScreen.scale-400.png",
            sizes: "2480x1200"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-16.png",
            sizes: "16x16"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-20.png",
            sizes: "20x20"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-24.png",
            sizes: "24x24"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-30.png",
            sizes: "30x30"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-32.png",
            sizes: "32x32"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-36.png",
            sizes: "36x36"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-40.png",
            sizes: "40x40"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-44.png",
            sizes: "44x44"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-48.png",
            sizes: "48x48"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-60.png",
            sizes: "60x60"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-64.png",
            sizes: "64x64"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-72.png",
            sizes: "72x72"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-80.png",
            sizes: "80x80"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-96.png",
            sizes: "96x96"
        },
        {
            src: "windows11/Square44x44Logo.targetsize-256.png",
            sizes: "256x256"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-16.png",
            sizes: "16x16"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-20.png",
            sizes: "20x20"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-24.png",
            sizes: "24x24"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-30.png",
            sizes: "30x30"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-32.png",
            sizes: "32x32"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-36.png",
            sizes: "36x36"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-40.png",
            sizes: "40x40"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-44.png",
            sizes: "44x44"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-48.png",
            sizes: "48x48"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-60.png",
            sizes: "60x60"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-64.png",
            sizes: "64x64"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-72.png",
            sizes: "72x72"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-80.png",
            sizes: "80x80"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-96.png",
            sizes: "96x96"
        },
        {
            src: "windows11/Square44x44Logo.altform-unplated_targetsize-256.png",
            sizes: "256x256"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png",
            sizes: "16x16"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png",
            sizes: "20x20"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png",
            sizes: "24x24"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png",
            sizes: "30x30"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png",
            sizes: "32x32"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png",
            sizes: "36x36"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png",
            sizes: "40x40"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png",
            sizes: "44x44"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png",
            sizes: "48x48"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png",
            sizes: "60x60"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png",
            sizes: "64x64"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png",
            sizes: "72x72"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png",
            sizes: "80x80"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png",
            sizes: "96x96"
        },
        {
            src: "windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png",
            sizes: "256x256"
        },
        {
            src: "android/android-launchericon-512-512.png",
            sizes: "512x512"
        },
        {
            src: "android/android-launchericon-192-192.png",
            sizes: "192x192"
        },
        {
            src: "android/android-launchericon-144-144.png",
            sizes: "144x144"
        },
        {
            src: "android/android-launchericon-96-96.png",
            sizes: "96x96"
        },
        {
            src: "android/android-launchericon-72-72.png",
            sizes: "72x72"
        },
        {
            src: "android/android-launchericon-48-48.png",
            sizes: "48x48"
        },
        {
            src: "ios/16.png",
            sizes: "16x16"
        },
        {
            src: "ios/20.png",
            sizes: "20x20"
        },
        {
            src: "ios/29.png",
            sizes: "29x29"
        },
        {
            src: "ios/32.png",
            sizes: "32x32"
        },
        {
            src: "ios/40.png",
            sizes: "40x40"
        },
        {
            src: "ios/50.png",
            sizes: "50x50"
        },
        {
            src: "ios/57.png",
            sizes: "57x57"
        },
        {
            src: "ios/58.png",
            sizes: "58x58"
        },
        {
            src: "ios/60.png",
            sizes: "60x60"
        },
        {
            src: "ios/64.png",
            sizes: "64x64"
        },
        {
            src: "ios/72.png",
            sizes: "72x72"
        },
        {
            src: "ios/76.png",
            sizes: "76x76"
        },
        {
            src: "ios/80.png",
            sizes: "80x80"
        },
        {
            src: "ios/87.png",
            sizes: "87x87"
        },
        {
            src: "ios/100.png",
            sizes: "100x100"
        },
        {
            src: "ios/114.png",
            sizes: "114x114"
        },
        {
            src: "ios/120.png",
            sizes: "120x120"
        },
        {
            src: "ios/128.png",
            sizes: "128x128"
        },
        {
            src: "ios/144.png",
            sizes: "144x144"
        },
        {
            src: "ios/152.png",
            sizes: "152x152"
        },
        {
            src: "ios/167.png",
            sizes: "167x167"
        },
        {
            src: "ios/180.png",
            sizes: "180x180"
        },
        {
            src: "ios/192.png",
            sizes: "192x192"
        },
        {
            src: "ios/256.png",
            sizes: "256x256"
        },
        {
            src: "ios/512.png",
            sizes: "512x512"
        },
        {
            src: "ios/1024.png",
            sizes: "1024x1024"
        }
    ],
    shortcuts: [
        {
            name: "Send",
            short_name: "Send",
            url: "/send",
            icons: [
                {
                    src: "/images/send.png",
                    sizes: "192x192",
                    type: "image/png"
                }
            ]
        },
        {
            name: "Receive",
            short_name: "Receive",
            url: "/receive",
            icons: [
                {
                    src: "/images/receive.png",
                    sizes: "192x192",
                    type: "image/png"
                }
            ]
        },
        {
            name: "Activity",
            short_name: "Activity",
            url: "/activity",
            icons: [
                {
                    src: "/images/activity.png",
                    sizes: "192x192",
                    type: "image/png"
                }
            ]
        }
    ]
};

export default manifest;


================================================
FILE: package.json
================================================
{
    "name": "mutiny-wallet",
    "version": "1.8.0",
    "license": "MIT",
    "packageManager": "pnpm@8.6.6",
    "scripts": {
        "dev": "vite",
        "build": "tsc && vite build",
        "check-types": "tsc --noemit",
        "eslint": "eslint src",
        "eslint-path": "eslint",
        "pre-commit": "pnpm eslint && pnpm run check-types && pnpm run format",
        "format": "prettier --write \"{.,src/**,e2e/**}/*.{ts,tsx,js,jsx,json,css,scss,md}\"",
        "check-format": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,scss,md}\""
    },
    "type": "module",
    "devDependencies": {
        "@capacitor/assets": "^3.0.5",
        "@capacitor/cli": "6.0.0",
        "@ianvs/prettier-plugin-sort-imports": "^4.2.1",
        "@playwright/test": "^1.42.1",
        "@typescript-eslint/eslint-plugin": "^7.4.0",
        "@typescript-eslint/parser": "^7.4.0",
        "autoprefixer": "^10.4.19",
        "eslint": "^8.57.0",
        "eslint-import-resolver-typescript": "3.6.1",
        "eslint-plugin-import": "2.29.1",
        "eslint-plugin-prettier": "5.1.3",
        "eslint-plugin-solid": "0.13.1",
        "postcss": "^8.4.38",
        "prettier": "^3.2.5",
        "prettier-plugin-tailwindcss": "^0.5.12",
        "tailwindcss": "^3.4.1",
        "typescript": "^5.4.5",
        "vite": "^5.2.10",
        "vite-plugin-comlink": "^4.0.3",
        "vite-plugin-pwa": "^0.19.8",
        "vite-plugin-solid": "^2.10.2",
        "vite-plugin-wasm": "^3.3.0",
        "workbox-window": "^7.0.0"
    },
    "dependencies": {
        "@capacitor-mlkit/barcode-scanning": "^6.0.0",
        "@capacitor/android": "^6.0.0",
        "@capacitor/app": "^6.0.0",
        "@capacitor/app-launcher": "^6.0.0",
        "@capacitor/clipboard": "^6.0.0",
        "@capacitor/core": "^6.0.0",
        "@capacitor/filesystem": "^6.0.0",
        "@capacitor/haptics": "^6.0.0",
        "@capacitor/ios": "^6.0.0",
        "@capacitor/network": "^6.0.0",
        "@capacitor/share": "^6.0.0",
        "@capacitor/status-bar": "^6.0.0",
        "@capacitor/toast": "^6.0.0",
        "@kobalte/core": "^0.12.6",
        "@kobalte/tailwindcss": "^0.9.0",
        "@modular-forms/solid": "^0.20.0",
        "@mutinywallet/mutiny-wasm": "1.7.13",
        "@solid-primitives/upload": "^0.0.117",
        "@solidjs/meta": "^0.29.3",
        "@solidjs/router": "^0.13.1",
        "capacitor-secure-storage-plugin": "^0.9.0",
        "comlink": "^4.4.1",
        "i18next": "^23.10.1",
        "i18next-browser-languagedetector": "^7.2.0",
        "i18next-http-backend": "^2.5.0",
        "lucide-solid": "^0.363.0",
        "qr-scanner": "^1.4.2",
        "solid-js": "^1.8.16",
        "solid-qr-code": "^0.0.8",
        "solid-transition-group": "^0.2.3"
    },
    "engines": {
        "node": ">=18"
    }
}


================================================
FILE: playwright.config.ts
================================================
import { defineConfig, devices } from "@playwright/test";

/**
 * Read environment variables from file.
 * https://github.com/motdotla/dotenv
 */
// require('dotenv').config();

/**
 * See https://playwright.dev/docs/test-configuration.
 */
export default defineConfig({
    testDir: "./e2e",
    /* Run tests in files in parallel */
    fullyParallel: true,
    /* Fail the build on CI if you accidentally left test.only in the source code. */
    forbidOnly: !!process.env.CI,
    /* Retry on CI only */
    retries: process.env.CI ? 2 : 0,
    /* Opt out of parallel tests on CI. */
    workers: process.env.CI ? 1 : undefined,
    /* Reporter to use. See https://playwright.dev/docs/test-reporters */
    reporter: "html",
    /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
    use: {
        /* Base URL to use in actions like `await page.goto('/')`. */
        // baseURL: 'http://127.0.0.1:3000',

        /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
        trace: "on-first-retry"
    },

    /* Configure projects for major browsers */
    projects: [
        // {
        //   name: "chromium",
        //   use: { ...devices["Desktop Chrome"] }
        // },

        // {
        //     name: "firefox",
        //     use: { ...devices["Desktop Firefox"] }
        // },

        // {
        //   name: "webkit",
        //   use: { ...devices["Desktop Safari"] }
        // },

        /* Test against mobile viewports. */
        {
            name: "Mobile Chrome",
            use: { ...devices["Pixel 5"] }
        }
        // {
        //     name: "Mobile Safari",
        //     use: { ...devices["iPhone 12"] }
        // }

        /* Test against branded browsers. */
        // {
        //   name: 'Microsoft Edge',
        //   use: { ...devices['Desktop Edge'], channel: 'msedge' },
        // },
        // {
        //   name: 'Google Chrome',
        //   use: { ..devices['Desktop Chrome'], channel: 'chrome' },
        // },
    ],

    /* Run your local dev server before starting the tests */
    webServer: {
        command: "pnpm run dev",
        url: "http://localhost:3420",
        reuseExistingServer: !process.env.CI
    }
});


================================================
FILE: postcss.config.cjs
================================================
module.exports = {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  };
  

================================================
FILE: prettier.config.mjs
================================================
/** @type {import("prettier").Options} */
export default {
    trailingComma: "none",
    tabWidth: 4,
    semi: true,
    singleQuote: false,
    arrowParens: "always",
    printWidth: 80,
    useTabs: false,

    plugins: [
        "@ianvs/prettier-plugin-sort-imports",
        "prettier-plugin-tailwindcss" // MUST come last
    ],

    tailwindFunctions: ["classList"],

    importOrder: ["<THIRD_PARTY_MODULES>", "", "^[~/]", "", "^[./]"]
};


================================================
FILE: public/.well-known/apple-app-site-association
================================================
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "X773Y823TN.com.mutinywallet.mutiny",
        "paths": ["*"]
      }
    ]
  }
}

================================================
FILE: public/.well-known/assetlinks.json
================================================
[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.mutinywallet.mutinywallet",
    "sha256_cert_fingerprints":
    ["F8:20:46:16:9B:79:AA:D1:2A:77:CA:D6:1F:0E:F6:A4:D7:72:3A:5E:DE:C6:70:18:E9:9E:B4:DA:0B:44:17:60"]
  }
}]


================================================
FILE: public/i18n/de.json
================================================
{
  "common": {
    "title": "Mutiny Wallet",
    "mutiny": "Mutiny",
    "nice": "Schön",
    "home": "Home",
    "e_sats": "eSATS",
    "e_sat": "eSAT",
    "sats": "SATS",
    "sat": "SAT",
    "fee": "Gebühren",
    "send": "Senden",
    "receive": "Erhalten",
    "dangit": "Mist",
    "back": "Zurück",
    "coming_soon": "(demnächst)",
    "copy": "Kopieren",
    "copied": "Kopiert",
    "continue": "Weiter",
    "error_unimplemented": "Nicht implementiert",
    "why": "Warum?",
    "private_tags": "Private Schlagwörter",
    "view_transaction": "Transaction anzeigen",
    "view_payment_details": "Zahlungsdetails anzeigen",
    "pending": "Ausstehend",
    "error_safe_mode": "Mutiny läuft im Safemode. Lightning ist deaktiviert.",
    "self_hosted": "Selbst gehostet"
  },
  "contacts": {
    "new": "neu",
    "add_contact": "Kontakt hinzufügen",
    "new_contact": "Neuer Kontakt",
    "create_contact": "Kontakt hinzufügen",
    "edit_contact": "Kontakt bearbeiten",
    "save_contact": "Kontakt speichern",
    "delete": "Löschen",
    "confirm_delete": "Bist du sicher, dass der Kontakt gelöscht werden soll?",
    "payment_history": "Zahlungshistorie",
    "no_payments": "Bisher keine Zahlungen mit",
    "edit": "Ändern",
    "pay": "Bezahlen",
    "name": "Name",
    "ln_address": "Lightning Adresse",
    "placeholder": "Satoshi",
    "lightning_address": "Lightning Adresse",
    "unimplemented": "Nicht implementiert",
    "not_available": "Noch nicht verfügbar",
    "error_name": "Wir benötigen mindestens einen Namen",
    "email_error": "Das sieht nicht nach einer Lightning Adresse aus",
    "npub_error": "Das sieht nicht nach einem Nostr npub aus",
    "error_ln_address_missing": "Neue Kontakte benötigen eine Lightning Adresse",
    "npub": "Nostr Npub",
    "link_to_nostr_sync": "Nostr Kontakte importieren"
  },
  "redeem": {
    "redeem_bitcoin": "Bitcoin einlösen",
    "lnurl_amount_message": "Auszubezahlender Betrag zwischen {{min}} und {{max}} Sats eingeben",
    "lnurl_redeem_failed": "Auszahlung abgebrochen",
    "lnurl_redeem_success": "Zahlung erhalten"
  },
  "receive": {
    "receive_bitcoin": "Bitcoin erhalten",
    "edit": "Bearbeiten",
    "checking": "Überprüfung",
    "choose_format": "Format wählen",
    "payment_received": "Zahlung erhalten",
    "payment_initiated": "Zahlung initiiert",
    "receive_add_the_sender": "Absenderinformationen hinzufügen",
    "keep_mutiny_open": "Bitte Mutiny geöffnet lassen, um die Zahlung abzuschliessen.",
    "choose_payment_format": "Zahlungsformat auswählen",
    "unified_label": "Kombiniert",
    "unified_caption": "Kombiniert eine Bitcoin Adresse mit einer Lightning Rechnung. Der Sender wählt die Zahlungsmethode.",
    "lightning_label": "Lightning Rechnung",
    "lightning_caption": "Ideal für kleine Beträge. In der Regel mit geringeren Gebühren als eine On-chain Transaktion.",
    "onchain_label": "Bitcoin Adresse",
    "onchain_caption": "On-chain Transaktion, wie es Satoshi getan hat. Ideal für sehr grosse Beträge.",
    "unified_setup_fee": "Bei einer Zahlung über Lightning wird eine Einrichtungsgebühr von {{amount}} SATS verrechnet.",
    "lightning_setup_fee": "Beim Erhalten einer Zahlung wird eine Lightning Einrichtungsgebühr von {{amount}} SATS verrechnet.",
    "amount": "Betrag",
    "fee": "+ Gebühr",
    "total": "Total",
    "spendable": "Verfügbar",
    "channel_size": "Kanalgrösse",
    "channel_reserve": "- Kanalreserve",
    "error_under_min_lightning": "Standardmässig über On-chain. Betrag ist zu klein für den Empfang deiner initiale Lightningzahlung.",
    "error_creating_unified": "Standardmässig über On-chain. Etwas ist schief gelaufen bei der Erstellung einer einheitlichen Adresse",
    "error_creating_address": "Etwas ist schief gelaufen bei der Erstellung einer On-chain Adresse",
    "amount_editable": {
      "receive_too_small": "Eine Lightning Einrichtungsgebühr wird möglicherweise vom angeforderten Betrag abgezogen.",
      "setup_fee_lightning": "Beim Bezahlen über Lightning wird eine Einrichtungsgebühr fällig.",
      "more_than_21m": "Es gibt nur 21 Millionen Bitcoin.",
      "set_amount": "Bestimme den Betrag",
      "max": "MAX",
      "fix_amounts": {
        "ten_k": "10k",
        "one_hundred_k": "100k",
        "one_million": "1m"
      },
      "del": "LÖSCHEN",
      "balance": "Kontostand"
    },
    "integrated_qr": {
      "onchain": "On-chain",
      "lightning": "Lightning",
      "unified": "Kombiniert",
      "gift": "Lightning Geschenk"
    },
    "remember_choice": "Auswahl merken",
    "what_for": "Wozu?"
  },
  "send": {
    "search": {
      "placeholder": "Name, Adresse, Rechnung",
      "paste": "Einfügen",
      "contacts": "Kontakte",
      "global_search": "Globale Suche",
      "no_results": "Keine Resultate für"
    },
    "sending": "Senden...",
    "confirm_send": "Sendung bestätigt",
    "contact_placeholder": "Empfänger speichern",
    "start_over": "Start über",
    "send_bitcoin": "Bitcoin senden",
    "paste": "Einfügen",
    "scan_qr": "QR scannen",
    "payment_initiated": "Zahlung initiiert",
    "payment_sent": "Zahlung gesendet",
    "destination": "Ziel",
    "no_payment_info": "Keine Zahlungsinfo",
    "progress_bar": {
      "of": "von",
      "sats_sent": "Sats gesendet"
    },
    "what_for": "Wozu?",
    "zap_note": "Zap Notiz",
    "error_low_balance": "Der Kontostand ist zu niedrig, um den Betrag zu überweisen.",
    "error_invoice_match": "Angeforderter Betrag, {{amount}} SATS, ist nicht identisch mit dem gesetzten Betrag.",
    "error_channel_reserves": "Kontostand zu niedrig.",
    "error_address": "Ungültige Lightning Adresse",
    "error_channel_reserves_explained": "Ein Teil deines Kontostands ist reserviert für Gebühren. Versuche kleinere Beträge zu senden oder den Kontostand zu erhöhen.",
    "error_clipboard": "Zwischenablage wird nicht unterstützt",
    "error_keysend": "Sendung fehlgeschlagen",
    "error_LNURL": "LNURL Bezahlung fehlgeschlagen",
    "error_expired": "Rechnung ist abgelaufen",
    "payjoin_send": "Das ist ein Payjoin! Die Meuterei wird so lange andauern, bis sich die Privatsphäre verbessert",
    "payment_pending": "Zahlung ausstehend",
    "payment_pending_description": "Es dauert eine Weile, aber es ist immer noch möglich, dass die Zahlung durchgeht. Bitte bei 'Aktivitäten' den aktuellen Status prüfen.",
    "hodl_invoice_warning": "Das ist eine Hodl Rechnung. Zahlungen zu Hodl Rechnungen kann zu Schliessungen von Kanälen mit hohen On-chain Gebühren führen. Bezahlung auf eigenes Risiko!",
    "private": "Privat",
    "anonzap": "Anon Zap"
  },
  "feedback": {
    "header": "Lasse uns dein Feedback da!",
    "received": "Feedback erhalten!",
    "thanks": "Danke, dass du uns wissen lässt, was läuft.",
    "more": "Hast du noch mehr zu sagen?",
    "tracking": "Mutiny verfolgt oder spioniert dein Verhalten nicht aus, daher ist dein Feedback unglaublich hilfreich.",
    "github": "Wir werden auf dein Feedback nicht antworten. Wenn du Unterstützung benötigst, bitte melden",
    "create_issue": "Ein GitHub-Problem erstellen.",
    "link": "Feedback?",
    "feedback_placeholder": "Fehler, Funktionswünsche, Feedback, etc.",
    "info_label": "Kontaktinformationen angeben",
    "info_caption": "Wenn du möchtest, dass wir dieses Problem weiterverfolgen",
    "email": "Email",
    "email_caption": "Burners willkommen",
    "nostr": "Nostr",
    "nostr_caption": "Deine neuste npub",
    "nostr_label": "Nostr npub oder NIP-05",
    "send_feedback": "Feedback senden",
    "invalid_feedback": "Bitte sage etwas!",
    "need_contact": "Wir benötigen eine Möglichkeit, um dich zu kontaktieren",
    "invalid_email": "Das sieht nicht nach einer Email-Adresse aus",
    "error": "Fehler beim Senden des Feedbacks {{error}}",
    "try_again": "Bitte versuche es später nochmals."
  },
  "activity": {
    "title": "Aktivität",
    "mutiny": "Mutiny",
    "wallet": "Wallet",
    "nostr": "Nostr",
    "view_all": "Alle anzeigen",
    "receive_some_sats_to_get_started": "Sats erhalten, um zu starten",
    "channel_open": "Kanal öffnen",
    "channel_close": "Kanal schliessen",
    "unknown": "Unbekannt",
    "import_contacts": "Kontakte von Nostr importieren, um zu sehen wer dir etwas zappt.",
    "coming_soon": "Demnächst",
    "private": "Privat",
    "anonymous": "Anonym",
    "from": "Von:",
    "transaction_details": {
      "lightning_receive": "Erhalten via Lightning",
      "lightning_send": "Gesendet via Lightning",
      "channel_open": "Kanal öffnen",
      "channel_close": "Kanal schliessen",
      "onchain_receive": "On-chain erhalten",
      "onchain_send": "On-chain gesendet",
      "paid": "Bezahlt",
      "unpaid": "Unbezahlt",
      "status": "Status",
      "date": "Datum",
      "tagged_to": "Markiert mit",
      "description": "Beschreibung",
      "fee": "Gebühr",
      "onchain_fee": "On-chain Gebühr",
      "invoice": "Rechnung",
      "payment_hash": "Zahlungs-Hash",
      "payment_preimage": "Vorbild",
      "txid": "Txid",
      "total": "Betrag angefordert",
      "balance": "Kontostand",
      "reserve": "Reserve",
      "peer": "Peer",
      "channel_id": "Kanal ID",
      "reason": "Grund",
      "confirmed": "Bestätigt",
      "unconfirmed": "Unbestätigt",
      "sweep_delay": "Es kann einige Tage dauern, bis dein Geld wieder in der Wallet verfügbar sind",
      "no_details": "Keine Kanaldetails gefunden, was bedeutet, dass dieser Kanal wahrscheinlich geschlossen wurde.",
      "back_home": "zurück zur Startseite"
    }
  },
  "scanner": {
    "paste": "Füge etwas ein",
    "cancel": "Abbrechen"
  },
  "settings": {
    "header": "Einstellungen",
    "support": "Lerne wie du Mutiny unterstützen kannst",
    "experimental_features": "Experimente",
    "debug_tools": "DEBUG TOOLS",
    "danger_zone": "Gefahrenzone",
    "general": "Allgemein",
    "version": "Version:",
    "admin": {
      "title": "Admin Seite",
      "caption": "Unsere internen Debug-Tools. Verwende sie mit Bedacht!",
      "header": "Geheime Debug Tools",
      "warning_one": "Wenn du weisst was du tust, bist du am richtigen Ort.",
      "warning_two": "Das sind interne Tools, die wir fürs Debuggen und Testen der App verwenden. Bitte vorsichtig sein!",
      "kitchen_sink": {
        "disconnect": "Trennen",
        "peers": "Peers",
        "no_peers": "Keine Peers",
        "refresh_peers": "Peers aktualisieren",
        "connect_peer": "Peer verbinden",
        "expect_a_value": "Ein Wert wird erwartet...",
        "connect": "Verbinden",
        "close_channel": "Kanal schliessen",
        "force_close": "Kanal schliessen erzwingen",
        "abandon_channel": "Kanal verlassen",
        "confirm_close_channel": "Bist du sicher, dass du den Kanal schliessen willst?",
        "confirm_force_close": "Bist du sicher, dass du die Kanalschliessung erzwingen willst? Deine Einlagen benötigen ein paar Tage, bis sie On-chain verfügbar sind.",
        "confirm_abandon_channel": "Bist du sicher, dass du diesen Kanal verlassen willst? Typischerweise wird diese Aktion nur ausgeführt, wenn die eröffnende Transaktion nicht bestätigt werden kann. Andernfalls wirst du deine Einlagen verlieren.",
        "channels": "Kanäle",
        "no_channels": "Keine Kanäle",
        "refresh_channels": "Kanäle aktualisieren",
        "pubkey": "Pubkey",
        "amount": "Betrag",
        "open_channel": "Kanal eröffnen",
        "nodes": "Nodes",
        "no_nodes": "Keine Nodes",
        "enable_zaps_to_hodl": "Zaps nach Hodl Invoices erlauben?",
        "zaps_to_hodl_desc": "Zaps nach Hodl Invoices kann zu erzwungenen Kanalschliessungen und hohen On-chain Gebühren führen. Benutzung auf eigene Gefahr!",
        "zaps_to_hodl_enable": "Hodl Zaps aktivieren",
        "zaps_to_hodl_disable": "Hodl Zaps deaktivieren"
      }
    },
    "backup": {
      "title": "Backup",
      "secure_funds": "Sorgen wir dafür, dass deine Einlagen gesichert werden.",
      "twelve_words_tip": "Wir werden dir 12 Wörter zeigen. Du schreibst die 12 Wörter auf.",
      "warning_one": "Wenn du deine Browser-Historie löschst oder dein Gerät verlierst sind diese 12 Wörter der einzige Weg, um dein Wallet wiederherzustellen.",
      "warning_two": "Mutiny ist eine selbst verwahrende Wallet. Du allein bist dafür verantwortlich...",
      "confirm": "Ich habe die Wörter aufgeschrieben",
      "responsibility": "Ich bin mir bewusst, dass meine Einlagen in meiner Verantwortung sind",
      "liar": "Ich lüge nicht, nur um das hinter mich zu bringen",
      "seed_words": {
        "reveal": "TIPPE, UM DIE SEED-WÖRTER ZU ENTDECKEN",
        "hide": "VERSTECKEN",
        "copy": "Gefährliches Kopieren in die Zwischenablage",
        "copied": "Kopiert!"
      }
    },
    "channels": {
      "title": "Lightning Kanäle",
      "outbound": "Outbound",
      "inbound": "Inbound",
      "reserve": "Reserve",
      "have_channels": "Du hast",
      "have_channels_one": "Lightning Kanal.",
      "have_channels_many": "lightning Kanäle.",
      "inbound_outbound_tip": "Outbound ist der Betrag, den du über Lightning ausgeben kannst. Inbound ist der Betrag, den du über Lightning empfangen kannst ohne eine Lightning Servicegebühr zu bezahlen.",
      "reserve_tip": "Um die 1% deines Kontostandes ist reserviert, um Lightning Gebühren zu bezahlen. Zusätzliche Reserven sind nötig, um Kanäle via Swap zu öffnen.",
      "no_channels": "Anscheinend hast du noch keine Kanäle. Erhalte ein paar Sats über Lightning, um zu starten. Oder transferiere ein paar on-chain Bitcoin in einen Lightning Kanal. Mache ruhig deine Hände schmutzig!",
      "close_channel": "Schliessen",
      "online_channels": "Online Kanäle",
      "offline_channels": "Offline Kanäle",
      "close_channel_confirm": "Wenn der Kanal geschlossen wird, werden die Einlagen nach on-chain transferiert. Dies führt zu einer on-chain Gebühr."
    },
    "connections": {
      "title": "Wallet Verbindungen",
      "error_name": "Name kann nicht leer sein",
      "error_connection": "Wallet Verbindung konnte nicht erstellt werden",
      "error_budget_zero": "Der Budgetrahmen muss grösser sein als 0",
      "add_connection": "Verbindung hinzufügen",
      "manage_connections": "Verbindungen verwalten",
      "manage_gifts": "Geschenke verwalten",
      "delete_connection": "Löschen",
      "new_connection": "Neue Verbindung",
      "edit_connection": "Verbindung bearbeiten",
      "new_connection_label": "Name",
      "new_connection_placeholder": "Mein Lieblings-Nostr-Client...",
      "create_connection": "Verbindung erstellen",
      "save_connection": "Änderungen speichern",
      "edit_budget": "Budgetrahmen bearbeiten",
      "open_app": "App öffnen",
      "open_in_nostr_client": "In Nostr-Client öffnen",
      "open_in_primal": "In Primal öffnen",
      "nostr_client_not_found": "Nostr-Client konnte nicht gefunden werden",
      "client_not_found_description": "Installiere einen Nostr-Client wie Primal, Amethyst oder Damus, um den Link zu öffnen.",
      "relay": "Relais",
      "authorize": "Autorisiere externe Dienste, um Zahlungen von deinem Wallet anzufordern. Passt hervorragend zu Nostr-Clients.",
      "pending_nwc": {
        "title": "Offene Anfragen",
        "approve_all": "Alle genehmigen",
        "deny_all": "Alle ablehnen"
      },
      "careful": "Vorsicht beim Teilen dieser Verbindung! Anfragen innerhalb des Budgetrahmens werden automatisch bezahlt.",
      "spent": "Ausgegeben",
      "remaining": "Übrig",
      "confirm_delete": "Bist du sicher, dass du diese Verbindung löschen willst?",
      "budget": "Budgetrahmen",
      "resets_every": "Alles zurücksetzen",
      "resubscribe_date": "Erneut abonnieren"
    },
    "emergency_kit": {
      "title": "Notfallbausatz",
      "caption": "Probleme im Wallet diagnostizieren und lösen.",
      "emergency_tip": "Wenn deine Wallet nicht mehr läuft, findest du hier ein paar Werkzeuge, um es zu debuggen und reparieren.",
      "questions": "Bitte melde dich, wenn du Fragen dazu hast, was diese Buttons tun",
      "link": "melde dich bei uns für Unterstützung.",
      "import_export": {
        "title": "Wallet-Status exportieren",
        "error_password": "Passwort wird benötigt",
        "error_read_file": "Fehler beim Lesen der Datei",
        "error_no_text": "Keinen Text in der Datei gefunden",
        "tip": "Du kannst deinen gesamten Status aus der Mutiniy-Wallet als Datei exportieren and sie in einem neuen Browser importieren. Normalerweise funktioniert es!",
        "caveat_header": "Wichtige Vorbehalte:",
        "caveat": "Nach dem Exportieren bitte keine Operationen mehr im originalen Browser vornehmen. Wenn doch, musst du den Export nochmals durchführen. Nach einem erfolgreichen Import wird empfohlen, den Status im originalen Browser zu löschen. So wird sichergestellt, dass es keine Konflikte gibt.",
        "save_state": "Status als Datei speichern",
        "import_state": "Status aus einer Datei importieren",
        "confirm_replace": "Möchtest du deinen Status ersetzen mit",
        "password": "Passwort eingeben, um zu entschlüsseln",
        "decrypt_wallet": "Wallet entschlüsseln"
      },
      "logs": {
        "title": "Debug-Protokolle herunterladen",
        "something_screwy": "Irgendetwas Verrücktes ist im Gange? Bitte schaue das Protokoll an!",
        "download_logs": "Protokolle herunterladen",
        "password": "Passwort eingeben, um zu entschlüsseln",
        "confirm_password_label": "Passwort bestätigen"
      },
      "delete_everything": {
        "delete": "Alles löschen",
        "confirm": "Damit löschst du den Status deiner Node. Das kann nicht rückgängig gemacht werden!",
        "deleted": "Gelöscht",
        "deleted_description": "Alle Daten gelöscht"
      }
    },
    "encrypt": {
      "title": "Passwort ändern",
      "caption": "Bitte zuerst ein Backup erstellen, um die Verschlüsselung zu entsperren",
      "header": "Deine Seed-Wörter entschlüsseln",
      "hot_wallet_warning": "Mutiny ist eine \"Hot Wallet\". Um zu funktionieren, benötigt es deine Seed-Wörter. Du kannst aber optional deine Seed-Wörter mit einem Passwort verschlüsseln.",
      "password_tip": "Auf diese Weise hat jemand, der Zugriff auf deinen Browser erhält, immer noch keinen Zugriff auf dein Geld.",
      "optional": "(optional)",
      "existing_password": "Bestehendes Passwort",
      "existing_password_caption": "Leer lassen, wenn du noch kein Passwort gesetzt hast.",
      "new_password_label": "Passwort",
      "new_password_placeholder": "Ein Passwort eingeben",
      "new_password_caption": "Dieses Passwort wird genutzt, um deine Seed-Wörter zu verschlüsseln. Wenn du es vergisst, musst du deine Seed-Wörter wieder eingeben, um an deine Einlagen zu gelangen. Du hast deine Seed-Wörter aufgeschrieben, oder?",
      "confirm_password_label": "Passwort bestätigen",
      "confirm_password_placeholder": "Passwort wiederholen",
      "encrypt": "Verschlüsseln",
      "skip": "Überspringen",
      "error_match": "Passwort stimmt nicht überein",
      "error_same_as_existingpassword": "Das neue Passwort darf nicht mit dem bestehenden Passwort übereinstimmen"
    },
    "decrypt": {
      "title": "Passwort eingeben",
      "decrypt_wallet": "Wallet entschlüsseln",
      "forgot_password_link": "Passwort vergessen?",
      "error_wrong_password": "Passwort ungültig"
    },
    "currency": {
      "title": "Währung",
      "caption": "Wähle dein bevorzugtes Währungspaar",
      "select_currency": "Währung auswählen",
      "select_currency_label": "Währungspaar",
      "select_currency_caption": "Wenn du eine neue Währung auswählst, wird das Wallet neu synchronisiert und die Preise aktualisiert",
      "request_currency_support_link": "Unterstützung anfordern für weitere Währungen",
      "error_unsupported_currency": "Bitte eine unterstützte Währung auswählen."
    },
    "language": {
      "title": "Sprache",
      "caption": "Wähle deine bevorzugte Sprache",
      "select_language": "Sprache auswählen",
      "select_language_label": "Sprache",
      "select_language_caption": "Wenn du eine neue Sprache auswählst, wird die Sprache in der Wallet geändert, unabhängig davon welche Sprache dein Browser verwendet",
      "request_language_support_link": "Fordere Unterstützung für weitere Sprachen an",
      "error_unsupported_language": "Bitte eine unterstützte Sprache auswählen."
    },
    "lnurl_auth": {
      "title": "LNURL Auth",
      "auth": "Auth",
      "expected": "Erwarte etwas wie LNURL..."
    },
    "plus": {
      "title": "Mutiny+",
      "join": "Mitmachen",
      "sats_per_month": "für {{amount}} Sats im Monat.",
      "lightning_balance": "Du benötigst mindestens {{amount}} Sats in deiner Lightning Wallet, um zu starten. Probiere es aus bevor du kaufst!",
      "restore": "Abonnement wiederherstellen",
      "ready_to_join": "Bereit zum Mitmachen",
      "click_confirm": "Klicke auf bestätigen, um deinen ersten Monat zu bezahlen.",
      "open_source": "Mutiny ist Open Source und selbstverwahrend.",
      "optional_pay": "Aber du kannst auch dafür bezahlen.",
      "paying_for": "Dafür bezahlen",
      "supports_dev": "unterstützt die laufende Entwicklung und ermöglicht den frühzeitigen Zugriff auf neue Features und Premium-Funktionalitäten:",
      "thanks": "Du bist Teil von Mutiny! Genieße die folgenden Vorteile:",
      "renewal_time": "Du erhältst eine Zahlungsaufforderung für die Verlängerung",
      "cancel": "Um dein Abonnement zu beenden, bezahle einfach nicht mehr. Du kannst Mutiny+ auch deaktivieren",
      "wallet_connection": "Wallet Verbindung.",
      "subscribe": "Einschreiben",
      "error_no_plan": "Kein Abonnement gefunden",
      "error_failure": "Konnte nicht abonniert werden",
      "error_no_subscription": "Kein bestehendes Abonnement gefunden",
      "error_expired_subscription": "Dein Abonnement ist abgelaufen, klicke auf Mitmachen, um es zu erneuern",
      "satisfaction": "Selbstgefällige Zufriedenheit",
      "gifting": "Schenken",
      "multi_device": "Zugriff mit mehreren Geräten",
      "ios_testflight": "Zugang zu iOS TestFlight",
      "more": "... und es wird noch mehr kommen",
      "cta_description": "Genieße frühzeitigen Zugriff auf neue Features und Premium-Funktionalitäten.",
      "cta_but_already_plus": "Danke für deinen Support!"
    },
    "restore": {
      "title": "Wiederherstellen",
      "all_twelve": "Du musst alle 12 Wörter eingeben",
      "wrong_word": "Falsches Wort",
      "paste": "Gefährliches Eingeben von der Zwischenablage",
      "confirm_text": "Bist du sicher, dass du diese Wallet wiederherstellen willst? Deine bestehende Wallet wird gelöscht!",
      "restore_tip": "Du kannst eine bestehende Mutiny Wallet mit deinen 12 Wörtern wiederherstellen. Das wird deine bestehende Wallet ersetzen, sei dir also sicher was du tust!",
      "multi_browser_warning": "Bitte nicht verschiedene Browser gleichzeitig nutzen.",
      "error_clipboard": "Zwischenablage wird nicht unterstützt",
      "error_word_number": "Falsche Anzahl Wörter",
      "error_invalid_seed": "Ungültige Seed Phrase"
    },
    "servers": {
      "title": "Server",
      "caption": "Vertraue uns nicht! Nutze deine eigenen Server, um Mutiny zu verwenden.",
      "link": "Lerne mehr über die Selbstverwahrung",
      "proxy_label": "Websockets Proxy",
      "proxy_caption": "Wie deine Lightning Node mit dem rest des Netzwerks kommuniziert.",
      "error_proxy": "Sollte eine URL sein, die mit wss:// beginnt",
      "esplora_label": "Esplora",
      "esplora_caption": "Block-Daten für On-chain Informationen.",
      "error_esplora": "Das sieht nicht nach einer gültigen URL aus",
      "rgs_label": "RGS",
      "rgs_caption": "Rapid Gossip Sync. Netzwerkdaten über das Lightning Netzwerk benutzt für das Routing.",
      "error_rgs": "Das sieht nicht nach einer gültigen URL aus",
      "lsp_label": "LSP",
      "lsp_caption": "Lightning Service Provider. Automatisches Eröffnen von Kanälen, um deine Inbound Liquidität sicherzustellen. Verpackt auch Rechnungen, um den Datenschutz zu gewähren.",
      "lsps_connection_string_label": "LSPS Connection String",
      "lsps_connection_string_caption": "Lightning Service Provider. Automatisches Eröffnen von Kanälen, um deine Inbound Liquidität sicherzustellen. Genutzt werden LSP Spezifikationen.",
      "error_lsps_connection_string": "Das sieht nicht nach einem Node Connection String aus",
      "lsps_token_label": "LSPS Token",
      "lsps_token_caption": "LSPS Token.  Wird benutzt, um zu identifizieren welche Wallet sich mit dem LSP verbindet",
      "lsps_valid_error": "Du kannst entweder ein LSP-Set oder LSPS Connection String und LSPS Token-Set haben, nicht beides.",
      "error_lsps_token": "Das sieht nicht nach einem validen Token aus",
      "storage_label": "Lagerung",
      "storage_caption": "Encrypted VSS Backup Service.",
      "error_lsp": "Das sieht nicht nach einer gültigen URL aus",
      "save": "Speichern"
    },
    "nostr_contacts": {
      "title": "Sync Nostr Kontakte",
      "npub_label": "Nostr npub",
      "npub_required": "Npub darf nicht leer sein",
      "sync": "Sync",
      "resync": "Resync",
      "remove": "Entfernen"
    },
    "manage_federations": {
      "title": "Federationen verwalten",
      "federation_code_label": "Federation Code",
      "federation_code_required": "Federation Code darf nicht leer sein",
      "federation_added_success": "Federation erfolgreich hinzugefügt",
      "federation_remove_confirm": "Bist du sicher, dass du diese Federation enfernen willst? Stelle zuerst sicher, dass all deine Einlagen auf deine Lightning Wallet transferiert wurden.",
      "add": "Hinzufügen",
      "remove": "Entfernen",
      "expires": "Läuft ab",
      "federation_id": "Federation ID",
      "description": "Mutiny unterstützt experimentell das Fedimint Protocoll. Du benötigst einen Einladungscode einer Federation, damit du diese Funktion nutzen kannst. Diese Einlagen sind zur Zeit aus der Ferne nicht gesichert. Das Speichern von Geld in einer Federation geschieht auf eigene Gefahr!",
      "learn_more": "Lerne mehr über Fedimint."
    },
    "gift": {
      "give_sats_link": "Verschenke Sats als Geschenk",
      "title": "Verschenken",
      "no_plus_caption": "Upgrade auf Mutiny+ um Verschenken zu aktivieren",
      "receive_too_small": "Deine erste Einzahlung muss mindestens {{amount}} SATS oder grösser sein.",
      "setup_fee_lightning": "Eine Lightning Einrichtungsgebühr wird beim Erhalten des Geschenks verrechnet.",
      "already_claimed": "Dieses Geschenk wurde bereits eingelöst",
      "sender_is_poor": "Der Kontostand des Senders ist nicht gross genug, um das Geschenk zu bezahlen.",
      "sender_timed_out": "Bezahlung des Geschenks ist abgelaufen. Der Sender ist vielleicht Offline, oder das Geschenk wurde bereits eingelöst.",
      "sender_generic_error": "Sender hat folgenden Fehler gesendet: {{error}}",
      "receive_header": "Dir hat jemand ein paar Sats geschenkt!",
      "receive_description": "Du musst etwas Spezielles sein. Um das Geld einzulösen, musst du nur den grossen Knopf drücken. Das Geld wird deiner Wallet hinzugefügt, sobald der Schenkende das nächste Mal online ist.",
      "receive_claimed": "Geschenk eingelöst! Du solltest das Geschenk demnächst in deinem Kontostand sehen.",
      "receive_cta": "Geschenk einlösen",
      "receive_try_again": "Nochmals versuchen",
      "send_header": "Geschenk erstellen",
      "send_explainer": "Übergebe das Geschenk gefüllt mit Sats. Erstelle eine Mutiny Geschenk-URL, die von jedem über einen Web-Browser eingelöst werden kann.",
      "send_name_required": "Das ist für deine Historie",
      "send_name_label": "Name des Empfängers",
      "send_header_claimed": "Geschenk erhalten!",
      "send_claimed": "Dein Geschenk wurde eingelöst. Danke fürs Teilen.",
      "send_sharable_header": "URL zum Teilen",
      "send_instructions": "Bitte diese Geschenk-URL deinem Empfänger senden, oder frage ihn, ob er diesen QR-Code mit seiner Wallet scannen kann.",
      "send_another": "Einen Weiteren erstellen",
      "send_small_warning": "Ein brandneuer Mutiny-Benutzer wird nicht fähig sein weniger als 100k Sats einzulösen.",
      "send_cta": "Ein Geschenk erstellen",
      "send_delete_button": "Geschenk löschen",
      "send_delete_confirm": "Bist du sicher, dass du dieses Geschenk löschen willst? Ist das dein Rugpull-Moment?",
      "send_tip": "Damit das Geschenk eingelöst werden kann, muss deine Mutiny Wallet geöffnet sein.",
      "need_plus": "Upgrade auf Mutiny+, um Schenken zu aktivieren. Durch Schenken kannst du eine Mutiny Geschenk-URL erstellen, die von jedem mit einem Webbrowser eingelöst werden kann."
    }
  },
  "swap": {
    "peer_not_found": "Peer nicht gefunden",
    "channel_too_small": "Es ist nicht klug einen Kanal zu eröffnen, der kleiner als {{amount}} Sats ist",
    "insufficient_funds": "Du hast nicht genügend Geld, um den Kanal zu eröffnen",
    "header": "Tausche in Lightning",
    "initiated": "Wechsel initialisiert",
    "sats_added": "+{{amount}} Sats werden deiner Lightning Wallet hinzugefügt",
    "use_existing": "Bitte den bestehenden Peer nutzen",
    "choose_peer": "Einen Peer wählen",
    "peer_connect_label": "Zu einem neuen Peer verbinden",
    "peer_connect_placeholder": "Peer Verbindgungs-String",
    "connect": "Verbinden",
    "connecting": "wird verbunden...",
    "confirm_swap": "Tausch bestätigen"
  },
  "swap_lightning": {
    "insufficient_funds": "Du hast nicht genug Geld, um nach Lightning zu wechseln",
    "header": "Tausche nach Lightning",
    "header_preview": "Vorschau des Tausches",
    "completed": "Tausch ausgeführt",
    "too_small": "Ungültiger Betrag eingegeben. Zum Tauschen benötigst du mindestens 100k Sats.",
    "sats_added": "+{{amount}} Sats wurden deiner Lightning Wallet hinzugefügt",
    "sats_fee": "+{{amount}} Sats Gebühren",
    "confirm_swap": "Tausch bestätigen",
    "preview_swap": "Vorschau der Tauschgebühr"
  },
  "reload": {
    "mutiny_update": "Mutiny Update",
    "new_version_description": "Neue Version von Mutiny wurde zwischengespeichert, Lade die App neu, um sie zu verwenden.",
    "reload": "Neu laden"
  },
  "error": {
    "title": "Fehler",
    "emergency_link": "Notfall-Kit.",
    "reload": "Neu laden",
    "restart": {
      "title": "Es ist etwas *besonders* Verrücktes im Gange? Stoppe die Nodes!",
      "start": "Start",
      "stop": "Stop"
    },
    "general": {
      "oh_no": "Oh nein!",
      "never_should_happen": "Das hätte nicht passieren dürfen",
      "try_reloading": "Versuche diese Seite neu zu laden oder klicke den \"Dangit\"-Knopf. Wenn du weiterhin Probleme hast,",
      "support_link": "kontaktiere uns für Unterstützung.",
      "getting_desperate": "Bist du verzweifelt? Versuche den"
    },
    "load_time": {
      "stuck": "Steckst du auf diesem Bildschirm fest? Versuche die Seite neu zu laden. Wann das nicht hilft, versuche den"
    },
    "not_found": {
      "title": "Nicht gefunden",
      "wtf_paul": "Das ist vielleicht Paul's Fehler."
    },
    "reset_router": {
      "payments_failing": "Werden Zahlungen nicht durchgeführt? Versuche den Lightning-Router zu resetten.",
      "reset_router": "Reset Router"
    },
    "resync": {
      "incorrect_balance": "On-chain Kontostand scheint inkorrekt? Versuche die On-chain Wallet neu zu synchen.",
      "resync_wallet": "Resync Wallet"
    },
    "on_boot": {
      "existing_tab": {
        "title": "Mehrere Tabs erkannt",
        "description": "Mutiny kann nur in einem Tab genutzt we
Download .txt
gitextract_b8zdsqe2/

├── .dockerignore
├── .eslintrc.cjs
├── .github/
│   └── workflows/
│       ├── android-build.yml
│       ├── android-prod.yml
│       ├── android-staging.yml
│       ├── code-quality.yml
│       ├── docker.yml
│       ├── ios-prod.yml
│       ├── ios-staging.yml
│       └── playwright.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── android/
│   ├── .gitignore
│   ├── app/
│   │   ├── .gitignore
│   │   ├── build.gradle
│   │   ├── capacitor.build.gradle
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── androidTest/
│   │       │   └── java/
│   │       │       └── com/
│   │       │           └── getcapacitor/
│   │       │               └── myapp/
│   │       │                   └── ExampleInstrumentedTest.java
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── java/
│   │       │   │   └── com/
│   │       │   │       └── mutinywallet/
│   │       │   │           └── mutinywallet/
│   │       │   │               └── MainActivity.java
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   └── ic_launcher_background.xml
│   │       │       ├── drawable-v24/
│   │       │       │   └── ic_launcher_foreground.xml
│   │       │       ├── layout/
│   │       │       │   └── activity_main.xml
│   │       │       ├── mipmap-anydpi-v26/
│   │       │       │   ├── ic_launcher.xml
│   │       │       │   └── ic_launcher_round.xml
│   │       │       ├── values/
│   │       │       │   ├── ic_launcher_background.xml
│   │       │       │   ├── strings.xml
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           └── file_paths.xml
│   │       └── test/
│   │           └── java/
│   │               └── com/
│   │                   └── getcapacitor/
│   │                       └── myapp/
│   │                           └── ExampleUnitTest.java
│   ├── build.gradle
│   ├── capacitor.settings.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   ├── settings.gradle
│   └── variables.gradle
├── capacitor.config.ts
├── default.conf
├── e2e/
│   ├── encrypt.spec.ts
│   ├── fedimint.spec.ts
│   ├── load.spec.ts
│   ├── restore.spec.ts
│   ├── roundtrip.spec.ts
│   ├── routes.spec.ts
│   └── utils.ts
├── flake.nix
├── index.html
├── ios/
│   ├── .gitignore
│   └── App/
│       ├── App/
│       │   ├── App.entitlements
│       │   ├── AppDelegate.swift
│       │   ├── Assets.xcassets/
│       │   │   ├── AppIcon.appiconset/
│       │   │   │   └── Contents.json
│       │   │   ├── Contents.json
│       │   │   └── Splash.imageset/
│       │   │       └── Contents.json
│       │   ├── Base.lproj/
│       │   │   ├── LaunchScreen.storyboard
│       │   │   └── Main.storyboard
│       │   └── Info.plist
│       ├── App.ipa
│       ├── App.xcodeproj/
│       │   ├── project.pbxproj
│       │   ├── project.xcworkspace/
│       │   │   └── contents.xcworkspacedata
│       │   └── xcshareddata/
│       │       └── xcschemes/
│       │           └── App.xcscheme
│       ├── App.xcworkspace/
│       │   ├── contents.xcworkspacedata
│       │   └── xcshareddata/
│       │       └── IDEWorkspaceChecks.plist
│       ├── Gemfile
│       ├── MyCustomViewController.swift
│       ├── Podfile
│       └── fastlane/
│           ├── .gitignore
│           ├── Appfile
│           ├── Fastfile
│           ├── Matchfile
│           └── README.md
├── justfile
├── manifest.ts
├── package.json
├── playwright.config.ts
├── postcss.config.cjs
├── prettier.config.mjs
├── public/
│   ├── .well-known/
│   │   ├── apple-app-site-association
│   │   └── assetlinks.json
│   ├── i18n/
│   │   ├── de.json
│   │   ├── en.json
│   │   ├── es.json
│   │   ├── fr.json
│   │   ├── hans.json
│   │   ├── hant.json
│   │   ├── it.json
│   │   ├── ko.json
│   │   └── pt.json
│   └── robots.txt
├── scripts/
│   └── errorsToTs.cjs
├── src/
│   ├── components/
│   │   ├── Activity.tsx
│   │   ├── ActivityDetailsModal.tsx
│   │   ├── Amount.tsx
│   │   ├── AmountEditable.tsx
│   │   ├── BalanceBox.tsx
│   │   ├── BigMoney.tsx
│   │   ├── ChooseCurrency.tsx
│   │   ├── ChooseLanguage.tsx
│   │   ├── ContactButton.tsx
│   │   ├── ContactEditor.tsx
│   │   ├── ContactForm.tsx
│   │   ├── ContactViewer.tsx
│   │   ├── DecryptDialog.tsx
│   │   ├── DeleteEverything.tsx
│   │   ├── EditProfileForm.tsx
│   │   ├── ErrorDisplay.tsx
│   │   ├── Fab.tsx
│   │   ├── Failure.tsx
│   │   ├── FederationInviteShower.tsx
│   │   ├── FederationPopup.tsx
│   │   ├── Fee.tsx
│   │   ├── FeeDisplay.tsx
│   │   ├── GenericItem.tsx
│   │   ├── HomeBalance.tsx
│   │   ├── HomePrompt.tsx
│   │   ├── HomeSubnav.tsx
│   │   ├── I18nProvider.tsx
│   │   ├── IOSbanner.tsx
│   │   ├── ImportExport.tsx
│   │   ├── ImportNsecForm.tsx
│   │   ├── InfoBox.tsx
│   │   ├── IntegratedQR.tsx
│   │   ├── JsonModal.tsx
│   │   ├── KitchenSink.tsx
│   │   ├── LabelCircle.tsx
│   │   ├── LightningAddressShower.tsx
│   │   ├── LoadingIndicator.tsx
│   │   ├── Logo.tsx
│   │   ├── Logs.tsx
│   │   ├── MoreInfoModal.tsx
│   │   ├── MutinyPlusCta.tsx
│   │   ├── NWCEditor.tsx
│   │   ├── NavBar.tsx
│   │   ├── NostrActivity.tsx
│   │   ├── PendingNwc.tsx
│   │   ├── Reader.tsx
│   │   ├── ReceiveWarnings.tsx
│   │   ├── Reload.tsx
│   │   ├── Restart.tsx
│   │   ├── ResyncOnchain.tsx
│   │   ├── SeedWords.tsx
│   │   ├── SetupErrorDisplay.tsx
│   │   ├── ShareCard.tsx
│   │   ├── SharpButton.tsx
│   │   ├── ShutdownPopup.tsx
│   │   ├── SimpleInput.tsx
│   │   ├── SocialActionRow.tsx
│   │   ├── Toaster.tsx
│   │   ├── ToggleHodl.tsx
│   │   ├── index.ts
│   │   ├── layout/
│   │   │   ├── BackLink.tsx
│   │   │   ├── BackPop.tsx
│   │   │   ├── Button.tsx
│   │   │   ├── ExternalLink.tsx
│   │   │   ├── LoadingSpinner.tsx
│   │   │   ├── Misc.tsx
│   │   │   ├── Radio.tsx
│   │   │   ├── SubtleButton.tsx
│   │   │   ├── TextField.tsx
│   │   │   └── index.ts
│   │   └── successfail/
│   │       ├── MegaCheck.tsx
│   │       ├── MegaClock.tsx
│   │       ├── MegaEx.tsx
│   │       ├── SuccessModal.tsx
│   │       └── index.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   └── context.ts
│   ├── index.tsx
│   ├── logic/
│   │   ├── browserCompatibility.ts
│   │   ├── errorDispatch.ts
│   │   ├── mutinyWalletSetup.ts
│   │   └── waila.ts
│   ├── root.css
│   ├── router.tsx
│   ├── routes/
│   │   ├── Chat.tsx
│   │   ├── EditProfile.tsx
│   │   ├── Feedback.tsx
│   │   ├── Main.tsx
│   │   ├── Profile.tsx
│   │   ├── Receive.tsx
│   │   ├── Redeem.tsx
│   │   ├── Request.tsx
│   │   ├── Scanner.tsx
│   │   ├── Search.tsx
│   │   ├── Send.tsx
│   │   ├── Swap.tsx
│   │   ├── SwapLightning.tsx
│   │   ├── Transfer.tsx
│   │   ├── [...404].tsx
│   │   ├── index.ts
│   │   ├── settings/
│   │   │   ├── Admin.tsx
│   │   │   ├── Backup.tsx
│   │   │   ├── Channels.tsx
│   │   │   ├── Connections.tsx
│   │   │   ├── Currency.tsx
│   │   │   ├── EmergencyKit.tsx
│   │   │   ├── Encrypt.tsx
│   │   │   ├── ImportProfile.tsx
│   │   │   ├── Language.tsx
│   │   │   ├── LightningAddress.tsx
│   │   │   ├── ManageFederations.tsx
│   │   │   ├── NostrKeys.tsx
│   │   │   ├── Plus.tsx
│   │   │   ├── Restore.tsx
│   │   │   ├── Root.tsx
│   │   │   ├── Servers.tsx
│   │   │   └── index.ts
│   │   └── setup/
│   │       ├── AddFederation.tsx
│   │       ├── ImportProfile.tsx
│   │       ├── NewProfile.tsx
│   │       ├── Restore.tsx
│   │       ├── Root.tsx
│   │       └── index.ts
│   ├── state/
│   │   └── megaStore.tsx
│   ├── styles/
│   │   └── dialogs.ts
│   ├── utils/
│   │   ├── baseUrl.ts
│   │   ├── bech32.ts
│   │   ├── blobToBase64.ts
│   │   ├── conversions.ts
│   │   ├── currencies.ts
│   │   ├── debounce.ts
│   │   ├── deepSignal.ts
│   │   ├── download.ts
│   │   ├── eify.ts
│   │   ├── fetchZaps.ts
│   │   ├── gradientHash.ts
│   │   ├── index.ts
│   │   ├── keypad.ts
│   │   ├── languages.ts
│   │   ├── mempoolTxUrl.ts
│   │   ├── nostr.ts
│   │   ├── objectToSearchParams.ts
│   │   ├── openLinkProgrammatically.ts
│   │   ├── platform.ts
│   │   ├── prettyPrintTime.ts
│   │   ├── subscriptions.ts
│   │   ├── timeout.ts
│   │   ├── typescript.ts
│   │   ├── useCopy.ts
│   │   ├── vibrate.ts
│   │   └── words.ts
│   ├── vite-env.d.ts
│   └── workers/
│       └── walletWorker.ts
├── tailwind.config.cjs
├── tsconfig.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (504 symbols across 142 files)

FILE: android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java
  class ExampleInstrumentedTest (line 16) | @RunWith(AndroidJUnit4.class)
    method useAppContext (line 19) | @Test

FILE: android/app/src/main/java/com/mutinywallet/mutinywallet/MainActivity.java
  class MainActivity (line 8) | public class MainActivity extends BridgeActivity {
    method onCreate (line 9) | @Override
    method onStart (line 14) | @Override

FILE: android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java
  class ExampleUnitTest (line 12) | public class ExampleUnitTest {
    method addition_isCorrect (line 14) | @Test

FILE: e2e/fedimint.spec.ts
  constant SIGNET_INVITE_CODE (line 5) | const SIGNET_INVITE_CODE =

FILE: e2e/routes.spec.ts
  function checkRoute (line 40) | async function checkRoute(

FILE: e2e/utils.ts
  function loadHome (line 3) | async function loadHome(page: Page) {
  function visitSettings (line 32) | async function visitSettings(page: Page) {

FILE: src/components/Activity.tsx
  type HackActivityType (line 42) | type HackActivityType =
  type IActivityItem (line 48) | interface IActivityItem {
  function fetchContactForNpub (line 59) | async function fetchContactForNpub(
  function UnifiedActivityItem (line 76) | function UnifiedActivityItem(props: {
  function NewContactModal (line 301) | function NewContactModal(props: { profile: PseudoContact; close: () => v...
  function CombinedActivity (line 368) | function CombinedActivity() {

FILE: src/components/ActivityDetailsModal.tsx
  type ChannelClosure (line 37) | interface ChannelClosure {
  type OnChainTx (line 44) | interface OnChainTx {
  constant OVERLAY (line 91) | const OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-sm";
  constant DIALOG_POSITIONER (line 92) | const DIALOG_POSITIONER =
  constant DIALOG_CONTENT (line 94) | const DIALOG_CONTENT =
  function LightningHeader (line 97) | function LightningHeader(props: { info: MutinyInvoice }) {
  function OnchainHeader (line 130) | function OnchainHeader(props: { info: OnChainTx; kind?: HackActivityType...
  function MiniStringShower (line 195) | function MiniStringShower(props: { text: string; hide?: boolean }) {
  function FormatPrettyPrint (line 226) | function FormatPrettyPrint(props: { ts: number }) {
  function LightningDetails (line 237) | function LightningDetails(props: { info: MutinyInvoice; tags?: TagItem }) {
  function OnchainDetails (line 297) | function OnchainDetails(props: {
  function ChannelCloseDetails (line 463) | function ChannelCloseDetails(props: { info: ChannelClosure }) {
  function ActivityDetailsModal (line 498) | function ActivityDetailsModal(props: {

FILE: src/components/Amount.tsx
  function prettyPrintAmount (line 9) | function prettyPrintAmount(n?: number | bigint): string {
  function AmountSats (line 16) | function AmountSats(props: {
  function AmountFiat (line 76) | function AmountFiat(props: {
  function AmountSmall (line 109) | function AmountSmall(props: {

FILE: src/components/AmountEditable.tsx
  type MethodChoice (line 21) | type MethodChoice = {
  function methodToIcon (line 26) | function methodToIcon(method: MethodChoice["method"]) {
  function handleSatsInput (line 82) | function handleSatsInput(e: InputEvent) {
  function handleFiatInput (line 102) | async function handleFiatInput(e: InputEvent) {
  function toggle (line 135) | function toggle(disabled: boolean) {
  function focus (line 145) | function focus() {
  function handleResize (line 182) | function handleResize(e: Event) {
  function MethodChooser (line 271) | function MethodChooser(props: {

FILE: src/components/BalanceBox.tsx
  function LoadingShimmer (line 19) | function LoadingShimmer(props: { center?: boolean; small?: boolean }) {
  constant STYLE (line 46) | const STYLE =
  function BalanceBox (line 49) | function BalanceBox(props: { loading?: boolean; small?: boolean }) {

FILE: src/components/BigMoney.tsx
  function BigScalingText (line 8) | function BigScalingText(props: {
  function SmallSubtleAmount (line 44) | function SmallSubtleAmount(props: {
  function BigMoney (line 74) | function BigMoney(props: {

FILE: src/components/ChooseCurrency.tsx
  type ChooseCurrencyForm (line 17) | type ChooseCurrencyForm = {
  constant COMBINED_OPTIONS (line 21) | const COMBINED_OPTIONS: Currency[] = [USD_OPTION, BTC_OPTION, ...FIAT_OP...
  function ChooseCurrency (line 23) | function ChooseCurrency() {

FILE: src/components/ChooseLanguage.tsx
  type ChooseLanguageForm (line 10) | type ChooseLanguageForm = {
  constant COMBINED_OPTIONS (line 14) | const COMBINED_OPTIONS: Language[] = [EN_OPTION, ...LANGUAGE_OPTIONS];
  function ChooseLanguage (line 16) | function ChooseLanguage() {

FILE: src/components/ContactButton.tsx
  function ContactButton (line 6) | function ContactButton(props: {

FILE: src/components/ContactEditor.tsx
  function ContactEditor (line 12) | function ContactEditor(props: {

FILE: src/components/ContactForm.tsx
  function ContactForm (line 29) | function ContactForm(props: {

FILE: src/components/ContactViewer.tsx
  type ContactFormValues (line 20) | type ContactFormValues = {
  function ContactViewer (line 26) | function ContactViewer(props: {

FILE: src/components/DecryptDialog.tsx
  function DecryptDialog (line 9) | function DecryptDialog() {

FILE: src/components/DeleteEverything.tsx
  function DeleteEverything (line 9) | function DeleteEverything(props: { emergency?: boolean }) {

FILE: src/components/EditProfileForm.tsx
  type EditableProfile (line 11) | type EditableProfile = {
  function EditProfileForm (line 17) | function EditProfileForm(props: {

FILE: src/components/ErrorDisplay.tsx
  function SimpleErrorDisplay (line 15) | function SimpleErrorDisplay(props: { error: Error }) {
  function ErrorDisplay (line 24) | function ErrorDisplay(props: { error: Error }) {

FILE: src/components/Fab.tsx
  function FabMenuItem (line 8) | function FabMenuItem(props: {
  function FabMenu (line 24) | function FabMenu(props: {
  function Fab (line 61) | function Fab(props: { onSearch: () => void; onScan: () => void }) {
  function MiniFab (line 114) | function MiniFab(props: {

FILE: src/components/Failure.tsx
  function Failure (line 7) | function Failure(props: { reason: string }) {

FILE: src/components/FederationInviteShower.tsx
  function FederationInviteShower (line 9) | function FederationInviteShower(props: {

FILE: src/components/FederationPopup.tsx
  function FederationPopup (line 9) | function FederationPopup() {

FILE: src/components/Fee.tsx
  function Fee (line 4) | function Fee(props: { amountSats?: bigint | number }) {

FILE: src/components/FeeDisplay.tsx
  function USDShower (line 23) | function USDShower(props: { amountSats: string; fee?: string }) {
  function add (line 69) | function add(a: string, b?: string) {
  function FeeDisplay (line 73) | function FeeDisplay(props: {

FILE: src/components/GenericItem.tsx
  function GenericItem (line 7) | function GenericItem(props: {

FILE: src/components/HomeBalance.tsx
  function HomeBalance (line 6) | function HomeBalance() {

FILE: src/components/HomePrompt.tsx
  function HomePrompt (line 37) | function HomePrompt() {

FILE: src/components/HomeSubnav.tsx
  function HomeSubnav (line 20) | function HomeSubnav() {

FILE: src/components/IOSbanner.tsx
  function IOSbanner (line 7) | function IOSbanner() {

FILE: src/components/ImportExport.tsx
  function ImportExport (line 19) | function ImportExport(props: { emergency?: boolean }) {

FILE: src/components/ImportNsecForm.tsx
  function ImportNsecForm (line 8) | function ImportNsecForm(props: { setup?: boolean }) {

FILE: src/components/IntegratedQR.tsx
  function KindIndicator (line 10) | function KindIndicator(props: { kind: ReceiveFlavor | "gift" | "lnAddres...
  function share (line 47) | async function share(receiveString: string) {
  function IntegratedQr (line 63) | function IntegratedQr(props: {

FILE: src/components/JsonModal.tsx
  function JsonModal (line 6) | function JsonModal(props: {

FILE: src/components/KitchenSink.tsx
  type RefetchPeersType (line 40) | type RefetchPeersType = (
  function PeerItem (line 44) | function PeerItem(props: { peer: MutinyPeer; refetchPeers: RefetchPeersT...
  function PeersList (line 83) | function PeersList() {
  function ConnectPeer (line 124) | function ConnectPeer(props: { refetchPeers: RefetchPeersType }) {
  type RefetchChannelsListType (line 169) | type RefetchChannelsListType = (
  type PendingChannelAction (line 173) | type PendingChannelAction = "close" | "force_close" | "abandon";
  function ChannelItem (line 175) | function ChannelItem(props: { channel: MutinyChannel; network?: Network ...
  function ChannelsList (line 278) | function ChannelsList() {
  function OpenChannel (line 326) | function OpenChannel(props: { refetchChannels: RefetchChannelsListType }) {
  function ListNodes (line 418) | function ListNodes() {
  function LSPS (line 447) | function LSPS(props: { initialSettings: MutinyWalletSettingStrings }) {
  function AsyncLSPSEditor (line 541) | function AsyncLSPSEditor() {
  function KitchenSink (line 556) | function KitchenSink() {

FILE: src/components/LabelCircle.tsx
  function Circle (line 7) | function Circle(props: {
  function LabelCircle (line 37) | function LabelCircle(props: {

FILE: src/components/LightningAddressShower.tsx
  function LightningAddressShower (line 10) | function LightningAddressShower(props: {

FILE: src/components/LoadingIndicator.tsx
  function LoadingBar (line 7) | function LoadingBar(props: { value: number; max: number }) {
  function LoadingIndicator (line 45) | function LoadingIndicator() {

FILE: src/components/Logo.tsx
  function Logo (line 7) | function Logo() {

FILE: src/components/Logs.tsx
  function Logs (line 8) | function Logs() {

FILE: src/components/MoreInfoModal.tsx
  function FeesModal (line 6) | function FeesModal() {

FILE: src/components/MutinyPlusCta.tsx
  function MutinyPlusCta (line 21) | function MutinyPlusCta() {

FILE: src/components/NWCEditor.tsx
  type BudgetInterval (line 39) | type BudgetInterval = "Day" | "Week" | "Month" | "Year";
  type BudgetForm (line 40) | type BudgetForm = {
  type FormMode (line 49) | type FormMode = "createnwa" | "createnwc" | "editnwc";
  type BudgetMode (line 50) | type BudgetMode = "fixed" | "editable";
  function parseNWA (line 52) | function parseNWA(nwaString?: string) {
  function mapNwaInterval (line 71) | function mapNwaInterval(interval: string): BudgetInterval | undefined {
  function mapIntervalToBudgetPeriod (line 90) | function mapIntervalToBudgetPeriod(
  function NWCEditor (line 105) | function NWCEditor(props: {
  function NWCEditorForm (line 364) | function NWCEditorForm(props: {

FILE: src/components/NavBar.tsx
  type ActiveTab (line 12) | type ActiveTab =
  function NavBarItem (line 21) | function NavBarItem(props: {
  function NavBar (line 43) | function NavBar(props: { activeTab: ActiveTab }) {

FILE: src/components/NostrActivity.tsx
  function Avatar (line 20) | function Avatar(props: { image_url?: string; large?: boolean }) {
  function NostrActivity (line 38) | function NostrActivity() {

FILE: src/components/PendingNwc.tsx
  type PendingItem (line 21) | type PendingItem = {
  function PendingNwc (line 29) | function PendingNwc() {

FILE: src/components/Reader.tsx
  function Reader (line 11) | function Reader(props: { onResult: (result: string) => void }) {

FILE: src/components/ReceiveWarnings.tsx
  function ReceiveWarnings (line 9) | function ReceiveWarnings(props: {

FILE: src/components/Reload.tsx
  function ReloadPrompt (line 11) | function ReloadPrompt() {

FILE: src/components/Restart.tsx
  function Restart (line 7) | function Restart() {

FILE: src/components/ResyncOnchain.tsx
  function ResyncOnchain (line 5) | function ResyncOnchain() {

FILE: src/components/SeedWords.tsx
  function SeedWords (line 7) | function SeedWords(props: {

FILE: src/components/SetupErrorDisplay.tsx
  function ErrorFooter (line 25) | function ErrorFooter() {
  function SetupErrorDisplay (line 36) | function SetupErrorDisplay(props: {

FILE: src/components/ShareCard.tsx
  constant STYLE (line 8) | const STYLE =
  function ShareButton (line 11) | function ShareButton(props: { receiveString: string; whiteBg?: boolean }) {
  function TruncateMiddle (line 37) | function TruncateMiddle(props: {
  function StringShower (line 57) | function StringShower(props: { text: string }) {
  function CopyButton (line 78) | function CopyButton(props: {
  function ShareCard (line 100) | function ShareCard(props: { text?: string }) {

FILE: src/components/SharpButton.tsx
  function SharpButton (line 3) | function SharpButton(props: {

FILE: src/components/ShutdownPopup.tsx
  function ShutdownPopup (line 7) | function ShutdownPopup() {

FILE: src/components/SimpleInput.tsx
  type SimpleInputProps (line 3) | type SimpleInputProps = {
  function SimpleInput (line 14) | function SimpleInput(props: SimpleInputProps) {

FILE: src/components/SocialActionRow.tsx
  function SocialActionRow (line 9) | function SocialActionRow(props: {

FILE: src/components/Toaster.tsx
  function Toaster (line 7) | function Toaster() {
  type ToastArg (line 17) | type ToastArg = { title: string; description: string } | Error;
  function showToast (line 19) | function showToast(arg: ToastArg) {
  function ToastItem (line 40) | function ToastItem(props: {

FILE: src/components/ToggleHodl.tsx
  function ToggleHodl (line 5) | function ToggleHodl() {

FILE: src/components/layout/BackLink.tsx
  function BackLink (line 6) | function BackLink(props: {
  function BackButton (line 24) | function BackButton(props: {

FILE: src/components/layout/BackPop.tsx
  type StateWithPrevious (line 7) | type StateWithPrevious = {
  function BackPop (line 11) | function BackPop(props: { default: string; title?: string }) {
  function UnstyledBackPop (line 45) | function UnstyledBackPop(props: {

FILE: src/components/layout/Button.tsx
  type CommonButtonStyleProps (line 9) | type CommonButtonStyleProps = {
  type ButtonProps (line 14) | interface ButtonProps
  type ButtonLinkProps (line 21) | interface ButtonLinkProps

FILE: src/components/layout/Misc.tsx
  function Checkbox (line 312) | function Checkbox(props: {
  function ModalCloseButton (line 344) | function ModalCloseButton() {
  constant SIMPLE_OVERLAY (line 352) | const SIMPLE_OVERLAY = "fixed inset-0 z-50 bg-black/50 backdrop-blur-lg";
  constant SIMPLE_DIALOG_POSITIONER (line 353) | const SIMPLE_DIALOG_POSITIONER =
  constant SIMPLE_DIALOG_CONTENT (line 355) | const SIMPLE_DIALOG_CONTENT =

FILE: src/components/layout/Radio.tsx
  type Choices (line 6) | type Choices = {
  function StyledRadioGroup (line 14) | function StyledRadioGroup(props: {

FILE: src/components/layout/SubtleButton.tsx
  function SubtleButton (line 5) | function SubtleButton(props: {

FILE: src/components/layout/TextField.tsx
  type TextFieldProps (line 6) | type TextFieldProps = {
  function TextField (line 30) | function TextField(props: TextFieldProps) {

FILE: src/components/successfail/MegaCheck.tsx
  function MegaCheck (line 3) | function MegaCheck() {

FILE: src/components/successfail/MegaClock.tsx
  function MegaClock (line 3) | function MegaClock() {

FILE: src/components/successfail/MegaEx.tsx
  function MegaEx (line 3) | function MegaEx() {

FILE: src/components/successfail/SuccessModal.tsx
  type SuccessModalProps (line 8) | type SuccessModalProps = {
  function SuccessModal (line 16) | function SuccessModal(props: SuccessModalProps) {

FILE: src/i18n/context.ts
  function useI18n (line 6) | function useI18n() {

FILE: src/logic/browserCompatibility.ts
  function checkBrowserCompatibility (line 1) | async function checkBrowserCompatibility(): Promise<boolean> {
  function openDatabase (line 35) | function openDatabase(): Promise<void> {

FILE: src/logic/errorDispatch.ts
  type MutinyError (line 6) | type MutinyError =
  function matchError (line 65) | function matchError(e: unknown): Error {

FILE: src/logic/mutinyWalletSetup.ts
  type Network (line 1) | type Network = "bitcoin" | "testnet" | "regtest" | "signet";
  type MutinyWalletSettingStrings (line 3) | type MutinyWalletSettingStrings = {
  constant SETTINGS_KEYS (line 21) | const SETTINGS_KEYS = [
  function getItemOrDefault (line 99) | function getItemOrDefault(
  function setItemIfNotDefault (line 113) | function setItemIfNotDefault(
  function getSettings (line 125) | async function getSettings() {
  function setSettings (line 166) | async function setSettings(newSettings: MutinyWalletSettingStrings) {
  function doubleInitDefense (line 178) | async function doubleInitDefense() {

FILE: src/logic/waila.ts
  type ParsedParams (line 4) | type ParsedParams = {
  function toParsedParams (line 22) | async function toParsedParams(

FILE: src/router.tsx
  function ChildrenOrError (line 74) | function ChildrenOrError(props: { children: JSX.Element }) {
  function Router (line 141) | function Router() {

FILE: src/routes/Chat.tsx
  type CombinedMessagesAndActivity (line 48) | type CombinedMessagesAndActivity =
  type FakeDirectMessage (line 53) | type FakeDirectMessage = {
  function isActivityItem (line 60) | function isActivityItem(content: unknown): content is IActivityItem {
  function isDirectMessage (line 64) | function isDirectMessage(content: unknown): content is FakeDirectMessage {
  function SingleMessage (line 68) | function SingleMessage(props: {
  function MessageList (line 223) | function MessageList(props: {
  function FixedChatHeader (line 305) | function FixedChatHeader(props: {
  function Chat (line 448) | function Chat() {

FILE: src/routes/EditProfile.tsx
  function EditProfile (line 16) | function EditProfile() {

FILE: src/routes/Feedback.tsx
  constant FEEDBACK_API (line 20) | const FEEDBACK_API = import.meta.env.VITE_FEEDBACK;
  function FeedbackLink (line 22) | function FeedbackLink(props: { setupError?: boolean }) {
  type FeedbackForm (line 42) | type FeedbackForm = {
  function formDataFromFeedbackForm (line 51) | async function formDataFromFeedbackForm(f: FeedbackForm) {
  function FeedbackForm (line 75) | function FeedbackForm(props: { onSubmitted: () => void }) {
  function Feedback (line 166) | function Feedback() {

FILE: src/routes/Main.tsx
  function WalletHeader (line 20) | function WalletHeader(props: { loading: boolean }) {
  function Main (line 70) | function Main() {

FILE: src/routes/Profile.tsx
  type UserProfile (line 21) | type UserProfile = {
  function Profile (line 28) | function Profile() {

FILE: src/routes/Receive.tsx
  type OnChainTx (line 47) | type OnChainTx = {
  type ReceiveFlavor (line 72) | type ReceiveFlavor = "lightning" | "onchain";
  type ReceiveState (line 73) | type ReceiveState = "edit" | "show" | "paid";
  type PaidState (line 74) | type PaidState = "lightning_paid" | "onchain_paid";
  function FeeWarning (line 76) | function FeeWarning(props: { fee: bigint; flavor: ReceiveFlavor }) {
  function FlavorChooser (line 90) | function FlavorChooser(props: {
  function ReceiveMethodHelp (line 140) | function ReceiveMethodHelp() {
  function Receive (line 160) | function Receive() {

FILE: src/routes/Redeem.tsx
  type RedeemState (line 36) | type RedeemState = "edit" | "paid";
  function Redeem (line 38) | function Redeem() {

FILE: src/routes/Request.tsx
  function RequestRoute (line 24) | function RequestRoute() {

FILE: src/routes/Scanner.tsx
  function Scanner (line 10) | function Scanner() {

FILE: src/routes/Search.tsx
  function Search (line 51) | function Search() {
  function ActualSearch (line 74) | function ActualSearch(props: { initialValue?: string }) {
  function GlobalSearch (line 407) | function GlobalSearch(props: {
  function SingleContact (line 498) | function SingleContact(props: {

FILE: src/routes/Send.tsx
  type SendSource (line 50) | type SendSource = "lightning" | "onchain";
  type PrivacyLevel (line 51) | type PrivacyLevel = "Public" | "Private" | "Anonymous" | "Not Available";
  type SentDetails (line 57) | type SentDetails = {
  function DestinationShower (line 66) | function DestinationShower(props: {
  function DestinationItem (line 139) | function DestinationItem(props: {
  function Send (line 160) | function Send() {

FILE: src/routes/Swap.tsx
  constant CHANNEL_FEE_ESTIMATE_ADDRESS (line 41) | const CHANNEL_FEE_ESTIMATE_ADDRESS =
  type PeerConnectForm (line 44) | type PeerConnectForm = {
  type ChannelOpenDetails (line 48) | type ChannelOpenDetails = {
  function Swap (line 53) | function Swap() {

FILE: src/routes/SwapLightning.tsx
  type SweepResultDetails (line 35) | type SweepResultDetails = {
  function SwapLightning (line 40) | function SwapLightning() {

FILE: src/routes/Transfer.tsx
  type TransferResultDetails (line 26) | type TransferResultDetails = {
  function Transfer (line 31) | function Transfer() {

FILE: src/routes/[...404].tsx
  function NotFound (line 6) | function NotFound() {

FILE: src/routes/settings/Admin.tsx
  function Admin (line 15) | function Admin() {

FILE: src/routes/settings/Backup.tsx
  function Quiz (line 19) | function Quiz(props: { setHasCheckedAll: (hasChecked: boolean) => void }) {
  function Backup (line 54) | function Backup() {

FILE: src/routes/settings/Channels.tsx
  function BalanceBar (line 36) | function BalanceBar(props: {
  function splitChannelNumbers (line 88) | function splitChannelNumbers(channel: MutinyChannel): {
  function SingleChannelItem (line 100) | function SingleChannelItem(props: { channel: MutinyChannel; online: bool...
  function LiquidityMonitor (line 176) | function LiquidityMonitor() {
  function Channels (line 306) | function Channels() {

FILE: src/routes/settings/Connections.tsx
  function Spending (line 39) | function Spending(props: { spent: number; remaining: number }) {
  function NwcDetails (line 74) | function NwcDetails(props: {
  function Nwc (line 203) | function Nwc() {
  function Connections (line 344) | function Connections() {

FILE: src/routes/settings/Currency.tsx
  function Currency (line 12) | function Currency() {

FILE: src/routes/settings/EmergencyKit.tsx
  function EmergencyStack (line 19) | function EmergencyStack() {
  function EmergencyKit (line 48) | function EmergencyKit() {

FILE: src/routes/settings/Encrypt.tsx
  type EncryptPasswordForm (line 20) | type EncryptPasswordForm = {
  function Encrypt (line 26) | function Encrypt() {

FILE: src/routes/settings/ImportProfile.tsx
  function ImportProfileSettings (line 8) | function ImportProfileSettings() {

FILE: src/routes/settings/Language.tsx
  function Language (line 12) | function Language() {

FILE: src/routes/settings/LightningAddress.tsx
  type HermesForm (line 40) | type HermesForm = {
  function HermesForm (line 51) | function HermesForm(props: { onSubmit: (name: string) => void }) {
  function LightningAddress (line 144) | function LightningAddress() {

FILE: src/routes/settings/ManageFederations.tsx
  type FederationForm (line 60) | type FederationForm = {
  type MutinyFederationIdentity (line 64) | type MutinyFederationIdentity = {
  type Metadata (line 75) | type Metadata = {
  type ResyncProgress (line 81) | type ResyncProgress = {
  type DiscoveredFederation (line 87) | type DiscoveredFederation = {
  type RefetchType (line 97) | type RefetchType = (
  function AddFederationForm (line 105) | function AddFederationForm(props: {
  function RecommendButton (line 239) | function RecommendButton(props: { fed: MutinyFederationIdentity }) {
  function ResyncLoadingBar (line 317) | function ResyncLoadingBar(props: { value: number; max: number }) {
  function FederationListItem (line 337) | function FederationListItem(props: {
  function ManageFederations (line 593) | function ManageFederations() {

FILE: src/routes/settings/NostrKeys.tsx
  function DeleteAccount (line 24) | function DeleteAccount() {
  function UnlinkAccount (line 72) | function UnlinkAccount() {
  function NostrKeys (line 114) | function NostrKeys() {

FILE: src/routes/settings/Plus.tsx
  function Perks (line 32) | function Perks(props: { alreadySubbed?: boolean }) {
  function PlusCTA (line 55) | function PlusCTA() {
  function Plus (line 167) | function Plus() {

FILE: src/routes/settings/Restore.tsx
  type SeedWordsForm (line 32) | type SeedWordsForm = {
  function validateWord (line 41) | function validateWord(word?: string): boolean {
  function SeedTextField (line 46) | function SeedTextField(props: TextFieldProps) {
  function TwelveWordsEntry (line 79) | function TwelveWordsEntry() {
  function Restore (line 248) | function Restore() {

FILE: src/routes/settings/Root.tsx
  function SettingsLinkList (line 19) | function SettingsLinkList(props: {
  function Settings (line 64) | function Settings() {

FILE: src/routes/settings/Servers.tsx
  function SettingsStringsEditor (line 49) | function SettingsStringsEditor(props: {
  function AsyncSettingsEditor (line 283) | function AsyncSettingsEditor() {
  function Servers (line 310) | function Servers() {

FILE: src/routes/setup/AddFederation.tsx
  function AddFederation (line 15) | function AddFederation() {

FILE: src/routes/setup/ImportProfile.tsx
  function ImportProfile (line 4) | function ImportProfile() {

FILE: src/routes/setup/NewProfile.tsx
  function NewProfile (line 14) | function NewProfile() {

FILE: src/routes/setup/Restore.tsx
  function SetupRestore (line 11) | function SetupRestore() {

FILE: src/routes/setup/Root.tsx
  function Setup (line 9) | function Setup() {

FILE: src/state/megaStore.tsx
  type LoadStage (line 33) | type LoadStage =
  type WalletWorker (line 41) | type WalletWorker = Remote<typeof import("../workers/walletWorker")>;
  method mutiny_plus (line 73) | get mutiny_plus(): boolean {
  method checkForSubscription (line 102) | async checkForSubscription(justPaid?: boolean): Promise<void> {
  method preSetup (line 118) | async preSetup(): Promise<boolean> {
  method setup (line 165) | async setup(password?: string): Promise<void> {
  method postSetup (line 305) | async postSetup(): Promise<void> {
  method deleteMutinyWallet (line 341) | async deleteMutinyWallet(): Promise<void> {
  method priceCheck (line 355) | async priceCheck(): Promise<void> {
  method sync (line 372) | async sync(): Promise<void> {
  method fetchPrice (line 397) | async fetchPrice(fiat: Currency): Promise<number | undefined> {
  method setScanResult (line 414) | setScanResult(scan_result: ParsedParams | undefined) {
  method setHasBackedUp (line 417) | setHasBackedUp() {
  method listTags (line 421) | async listTags(): Promise<TagItem[] | undefined> {
  method saveFiat (line 429) | async saveFiat(fiat: Currency) {
  method saveLanguage (line 437) | saveLanguage(lang: string) {
  method setPreferredInvoiceType (line 441) | setPreferredInvoiceType(type: "unified" | "lightning" | "onchain") {
  method handleIncomingString (line 444) | async handleIncomingString(
  method setTestFlightPromptDismissed (line 504) | setTestFlightPromptDismissed() {
  method toggleHodl (line 508) | toggleHodl() {
  method refreshFederations (line 513) | async refreshFederations() {
  method cycleBalanceView (line 536) | cycleBalanceView() {
  method checkForExistingTab (line 548) | async checkForExistingTab() {
  method clearExpirationWarning (line 576) | clearExpirationWarning() {
  method clearShutdownWarning (line 580) | clearShutdownWarning() {
  type MegaStoreContextType (line 588) | type MegaStoreContextType = ReturnType<typeof makeMegaStoreContext>;

FILE: src/styles/dialogs.ts
  constant DIALOG_POSITIONER (line 1) | const DIALOG_POSITIONER =
  constant DIALOG_CONTENT (line 3) | const DIALOG_CONTENT =

FILE: src/utils/baseUrl.ts
  function baseUrlAccountingForNative (line 4) | function baseUrlAccountingForNative(network?: string) {

FILE: src/utils/bech32.ts
  constant ALPHABET (line 2) | const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
  constant ALPHABET_MAP (line 4) | const ALPHABET_MAP: { [key: string]: number } = {};
  function polymodStep (line 10) | function polymodStep(pre: number): number {
  function prefixChk (line 22) | function prefixChk(prefix: string): number | string {
  function convert (line 51) | function convert(
  function toWords (line 84) | function toWords(bytes: ArrayLike<number>): number[] {
  function fromWordsUnsafe (line 88) | function fromWordsUnsafe(words: ArrayLike<number>): number[] | undefined {
  function fromWords (line 93) | function fromWords(words: ArrayLike<number>): number[] {
  function getLibraryFromEncoding (line 100) | function getLibraryFromEncoding(encoding: "bech32" | "bech32m"): BechLib {
  function bech32WordsToUrl (line 206) | function bech32WordsToUrl(words: number[]) {
  type Decoded (line 224) | interface Decoded {
  type BechLib (line 228) | interface BechLib {

FILE: src/utils/blobToBase64.ts
  function blobToBase64 (line 1) | async function blobToBase64(file: File): Promise<string> {

FILE: src/utils/conversions.ts
  function satsToFiat (line 12) | async function satsToFiat(
  function satsToFormattedFiat (line 47) | async function satsToFormattedFiat(
  function fiatToSats (line 83) | async function fiatToSats(

FILE: src/utils/currencies.ts
  type Currency (line 1) | interface Currency {
  constant BTC_OPTION (line 15) | const BTC_OPTION: Currency = {
  constant USD_OPTION (line 22) | const USD_OPTION: Currency = {
  constant FIAT_OPTIONS (line 46) | const FIAT_OPTIONS: Currency[] = [

FILE: src/utils/debounce.ts
  type ScheduleCallback (line 5) | type ScheduleCallback = <Args extends unknown[]>(
  type Scheduled (line 10) | interface Scheduled<Args extends unknown[]> {

FILE: src/utils/deepSignal.ts
  function createDeepSignal (line 6) | function createDeepSignal<T>(value: T): Signal<T> {

FILE: src/utils/download.ts
  function downloadTextFile (line 8) | async function downloadTextFile(

FILE: src/utils/eify.ts
  function eify (line 2) | function eify(e: unknown): Error {

FILE: src/utils/fetchZaps.ts
  constant ZAPPLE_PAY_NPUB (line 11) | const ZAPPLE_PAY_NPUB =
  constant ZAPPLE_PAY_HEXPUB (line 14) | const ZAPPLE_PAY_HEXPUB =
  type NostrEvent (line 17) | type NostrEvent = {
  type SimpleZapItem (line 27) | type SimpleZapItem = {
  type NostrProfile (line 40) | type NostrProfile = {
  function findByTag (line 50) | function findByTag(tags: string[][], tag: string): string | undefined {
  function getZapKind (line 63) | function getZapKind(event: NostrEvent): "public" | "private" | "anonymou...
  function simpleZapFromEvent (line 78) | async function simpleZapFromEvent(
  constant PRIMAL_API (line 145) | const PRIMAL_API = import.meta.env.VITE_PRIMAL;
  type PrimalResponse (line 147) | type PrimalResponse = NostrEvent | NostrProfile;
  function fetchZapsFromPrimal (line 149) | async function fetchZapsFromPrimal(
  function actuallyFetchNostrProfile (line 306) | async function actuallyFetchNostrProfile(hexpub: string) {
  type PseudoContact (line 337) | type PseudoContact = {
  function searchProfiles (line 346) | async function searchProfiles(query: string): Promise<PseudoContact[]> {
  function profileToPseudoContact (line 382) | function profileToPseudoContact(profile: NostrProfile): PseudoContact {

FILE: src/utils/gradientHash.ts
  function generateGradient (line 3) | async function generateGradient(str: string) {
  function gradientsPerContact (line 16) | async function gradientsPerContact(contacts: TagItem[]) {

FILE: src/utils/keypad.ts
  function toDisplayHandleNaN (line 8) | function toDisplayHandleNaN(
  function fiatInputSanitizer (line 75) | function fiatInputSanitizer(input: string, maxDecimals: number): string {
  function satsInputSanitizer (line 106) | function satsInputSanitizer(input: string): string {
  function btcFloatRounding (line 115) | function btcFloatRounding(localValue: string): string {

FILE: src/utils/languages.ts
  type Language (line 1) | interface Language {
  constant EN_OPTION (line 6) | const EN_OPTION: Language = {
  constant LANGUAGE_OPTIONS (line 12) | const LANGUAGE_OPTIONS: Language[] = [

FILE: src/utils/mempoolTxUrl.ts
  function mempoolTxUrl (line 3) | function mempoolTxUrl(txid?: string, network?: Network) {

FILE: src/utils/nostr.ts
  type NostrTag (line 3) | type NostrTag = string[];
  type NostrKind (line 4) | enum NostrKind {
  function hexpubFromNpub (line 47) | async function hexpubFromNpub(
  function getPrimalImageUrl (line 67) | function getPrimalImageUrl(image_url?: string): string | undefined {
  constant DEFAULT_NOSTR_NAME (line 76) | const DEFAULT_NOSTR_NAME = "Anon";

FILE: src/utils/objectToSearchParams.ts
  function objectToSearchParams (line 1) | function objectToSearchParams<

FILE: src/utils/openLinkProgrammatically.ts
  function openLinkProgrammatically (line 7) | async function openLinkProgrammatically(

FILE: src/utils/platform.ts
  function iosNotNative (line 5) | function iosNotNative() {

FILE: src/utils/prettyPrintTime.ts
  function prettyPrintTime (line 3) | function prettyPrintTime(ts: number) {
  function timeAgo (line 16) | function timeAgo(
  function veryShortTimeStamp (line 49) | function veryShortTimeStamp(ts?: number | bigint) {

FILE: src/utils/subscriptions.ts
  constant GRACE (line 1) | const GRACE = 60 * 60 * 24 * 3;
  function subscriptionValid (line 3) | function subscriptionValid(subscriptionExpiresTimestamp?: number) {
  function isFreeGiftingDay (line 9) | function isFreeGiftingDay() {
  function isThanksgiving (line 17) | function isThanksgiving(today: Date) {

FILE: src/utils/typescript.ts
  type Result (line 1) | type Result<T, E = Error> =

FILE: src/utils/useCopy.ts
  type UseCopyProps (line 7) | type UseCopyProps = {
  type CopyFn (line 10) | type CopyFn = (text: string) => Promise<void>;

FILE: src/utils/words.ts
  constant WORDS_EN (line 1) | const WORDS_EN: Set<string> = new Set([

FILE: src/workers/walletWorker.ts
  function destructureInvoice (line 31) | function destructureInvoice(invoice: MutinyInvoice): MutinyInvoice {
  function checkForWasm (line 56) | async function checkForWasm() {
  function initializeWasm (line 76) | async function initializeWasm() {
  function setupMutinyWallet (line 92) | async function setupMutinyWallet(
  function get_balance (line 188) | async function get_balance(): Promise<MutinyBalance> {
  function list_federations (line 203) | async function list_federations(): Promise<MutinyFederationIdentity[]> {
  function check_subscribed (line 217) | async function check_subscribed(): Promise<bigint | undefined> {
  function stop (line 227) | async function stop(): Promise<void> {
  function delete_all (line 237) | async function delete_all(): Promise<void> {
  function get_bitcoin_price (line 246) | async function get_bitcoin_price(fiat: string): Promise<number> {
  function get_tag_items (line 251) | async function get_tag_items(): Promise<TagItem[]> {
  function get_network (line 260) | async function get_network(): Promise<string> {
  function get_nostr_profile (line 269) | async function get_nostr_profile(): Promise<NostrMetadata | undefined> {
  function get_activity (line 283) | async function get_activity(
  function get_contact_for_npub (line 291) | async function get_contact_for_npub(
  function create_new_contact (line 299) | async function create_new_contact(
  function get_tag_item (line 316) | async function get_tag_item(id: string): Promise<TagItem | undefined> {
  function get_label_activity (line 327) | async function get_label_activity(
  function get_dm_conversation (line 343) | async function get_dm_conversation(
  function get_invoice (line 359) | async function get_invoice(
  function get_contacts_sorted (line 372) | async function get_contacts_sorted(limit?: number): Promise<TagItem[]> {
  function edit_contact (line 382) | async function edit_contact(
  function delete_contact (line 393) | async function delete_contact(id: string): Promise<void> {
  function follow_npub (line 402) | async function follow_npub(npub: string): Promise<void> {
  function unfollow_npub (line 413) | async function unfollow_npub(npub: string): Promise<void> {
  function send_dm (line 423) | async function send_dm(
  function get_npub (line 435) | async function get_npub(): Promise<string | undefined> {
  function decode_invoice (line 447) | async function decode_invoice(
  function create_bip21 (line 488) | async function create_bip21(
  function create_invoice (line 509) | async function create_invoice(
  function estimate_sweep_tx_fee (line 527) | async function estimate_sweep_tx_fee(
  function estimate_tx_fee (line 542) | async function estimate_tx_fee(
  function decode_lnurl (line 557) | async function decode_lnurl(lnurl: string): Promise<LnUrlParams> {
  function pay_invoice (line 574) | async function pay_invoice(
  function lnurl_pay (line 595) | async function lnurl_pay(
  function sweep_wallet (line 625) | async function sweep_wallet(
  function send_payjoin (line 638) | async function send_payjoin(
  function send_to_address (line 664) | async function send_to_address(
  function keysend (line 688) | async function keysend(
  function get_invoice_by_hash (line 705) | async function get_invoice_by_hash(
  function get_channel_closure (line 717) | async function get_channel_closure(
  function get_transaction (line 734) | async function get_transaction(txid: string): Promise<ActivityItem> {
  function get_new_address (line 748) | async function get_new_address(
  function check_address (line 765) | async function check_address(address: string): Promise<OnChainTx> {
  function list_channels (line 774) | async function list_channels(): Promise<MutinyChannel[]> {
  function setup_new_profile (line 788) | async function setup_new_profile(
  function discover_federations (line 807) | async function discover_federations(): Promise<
  function has_recommended_federation (line 819) | async function has_recommended_federation(
  function new_federation (line 832) | async function new_federation(inviteCode: string): Promise<unknown> {
  type NostrMetadata (line 837) | type NostrMetadata = {
  function edit_nostr_profile (line 854) | async function edit_nostr_profile(
  function upload_profile_pic (line 876) | async function upload_profile_pic(data: string): Promise<string> {
  function get_pending_nwc_invoices (line 885) | async function get_pending_nwc_invoices(): Promise<
  function delete_nwc_profile (line 913) | async function delete_nwc_profile(index: number): Promise<void> {
  function get_nwc_profiles (line 921) | async function get_nwc_profiles(): Promise<NwcProfile[]> {
  function approve_nostr_wallet_auth (line 945) | async function approve_nostr_wallet_auth(
  function get_nwc_profile (line 958) | async function get_nwc_profile(index: number): Promise<NwcProfile> {
  function approve_invoice (line 970) | async function approve_invoice(hash: string): Promise<void> {
  function deny_invoice (line 979) | async function deny_invoice(hash: string): Promise<void> {
  function deny_all_pending_nwc (line 987) | async function deny_all_pending_nwc(): Promise<void> {
  function set_nwc_profile_budget (line 999) | async function set_nwc_profile_budget(
  function set_nwc_profile_require_approval (line 1019) | async function set_nwc_profile_require_approval(
  function create_nwc_profile (line 1033) | async function create_nwc_profile(
  function create_budget_nwc_profile (line 1050) | async function create_budget_nwc_profile(
  function disconnect_peer (line 1072) | async function disconnect_peer(pubkey: string): Promise<void> {
  function delete_peer (line 1083) | async function delete_peer(pubkey: string): Promise<void> {
  function list_peers (line 1091) | async function list_peers(): Promise<MutinyPeer[]> {
  function connect_to_peer (line 1101) | async function connect_to_peer(
  function close_channel (line 1122) | async function close_channel(
  function remove_federation (line 1135) | async function remove_federation(federation_id: string): Promise<void> {
  function resync_federation (line 1144) | async function resync_federation(federation_id: string): Promise<void> {
  function get_federation_resync_progress (line 1153) | async function get_federation_resync_progress(
  function open_channel (line 1170) | async function open_channel(
  function list_nodes (line 1182) | async function list_nodes(): Promise<string[]> {
  function change_lsp (line 1196) | async function change_lsp(
  type LspConfig (line 1204) | type LspConfig = {
  function get_configured_lsp (line 1215) | async function get_configured_lsp(): Promise<LspConfig> {
  function reset_onchain_tracker (line 1225) | async function reset_onchain_tracker(): Promise<void> {
  function start (line 1234) | async function start(): Promise<void> {
  function lnurl_auth (line 1243) | async function lnurl_auth(lnurl: string): Promise<void> {
  type PlanDetails (line 1248) | type PlanDetails = {
  function get_subscription_plans (line 1257) | async function get_subscription_plans(): Promise<PlanDetails[]> {
  function subscribe_to_plan (line 1268) | async function subscribe_to_plan(id: number): Promise<MutinyInvoice> {
  function pay_subscription_invoice (line 1279) | async function pay_subscription_invoice(
  function change_nostr_keys (line 1292) | async function change_nostr_keys(
  function delete_profile (line 1303) | async function delete_profile(): Promise<void> {
  function export_nsec (line 1311) | async function export_nsec(): Promise<string | undefined> {
  function show_seed (line 1320) | async function show_seed(): Promise<string> {
  function claim_single_use_nwc (line 1330) | async function claim_single_use_nwc(
  function lnurl_withdraw (line 1344) | async function lnurl_withdraw(
  function check_lnurl_name (line 1355) | async function check_lnurl_name(): Promise<string | undefined> {
  function check_available_lnurl_name (line 1364) | async function check_available_lnurl_name(
  function reserve_lnurl_name (line 1375) | async function reserve_lnurl_name(name: string): Promise<void> {
  function recommend_federation (line 1385) | async function recommend_federation(
  function delete_federation_recommendation (line 1397) | async function delete_federation_recommendation(
  function get_federation_balances (line 1407) | async function get_federation_balances(): Promise<FederationBalances> {
  function change_password (line 1426) | async function change_password(
  function convert_sats_to_btc (line 1438) | async function convert_sats_to_btc(sats: bigint): Promise<number> {
  function convert_btc_to_sats (line 1447) | async function convert_btc_to_sats(btc: number): Promise<bigint> {
  function has_node_manager (line 1456) | async function has_node_manager(): Promise<boolean> {
  function npub_to_hexpub (line 1465) | async function npub_to_hexpub(npub: string): Promise<string> {
  function nsec_to_npub (line 1474) | async function nsec_to_npub(nsec: string): Promise<string> {
  function hexpub_to_npub (line 1483) | async function hexpub_to_npub(hexpub: string): Promise<string> {
  function restore_mnemonic (line 1497) | async function restore_mnemonic(
  function import_json (line 1509) | async function import_json(json: string): Promise<void> {
  function export_json (line 1518) | async function export_json(password?: string): Promise<string> {
  function get_logs (line 1526) | async function get_logs(): Promise<string[]> {
  function get_device_lock_remaining_secs (line 1533) | async function get_device_lock_remaining_secs(
  function sweep_all_to_channel (line 1553) | async function sweep_all_to_channel(
  function estimate_sweep_channel_open_fee (line 1565) | async function estimate_sweep_channel_open_fee(
  function sweep_federation_balance_to_invoice (line 1577) | async function sweep_federation_balance_to_invoice(
  function create_sweep_federation_invoice (line 1595) | async function create_sweep_federation_invoice(
  function parse_params (line 1608) | async function parse_params(params: string): Promise<PaymentParams> {
Condensed preview — 250 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,326K chars).
[
  {
    "path": ".dockerignore",
    "chars": 62,
    "preview": "node_modules\n.gitignore\n.github\n*.md\ndist\njustfile\n.env.local\n"
  },
  {
    "path": ".eslintrc.cjs",
    "chars": 1715,
    "preview": "module.exports = {\n    env: {\n        browser: true,\n        es2021: true,\n        node: true\n    },\n    extends: [\n    "
  },
  {
    "path": ".github/workflows/android-build.yml",
    "chars": 2462,
    "preview": "name: Build Android\n\non:\n  push:\n    branches:\n      - master\n      - prod\n  pull_request:\n    branches:\n      - '*'\n\njo"
  },
  {
    "path": ".github/workflows/android-prod.yml",
    "chars": 5748,
    "preview": "name: Release Android Prod\n\non:\n  push:\n    tags:\n    - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10\n\njobs:\n "
  },
  {
    "path": ".github/workflows/android-staging.yml",
    "chars": 4547,
    "preview": "name: Release Android Staging\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n\njobs:\n  build:\n    name: B"
  },
  {
    "path": ".github/workflows/code-quality.yml",
    "chars": 1250,
    "preview": "name: Code Quality Check\non:\n  push:\n    branches:\n      - master\n      - prod\n  pull_request:\n    branches:\n      - '*'"
  },
  {
    "path": ".github/workflows/docker.yml",
    "chars": 1318,
    "preview": "name: Create and publish Docker images\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n  release:\n    typ"
  },
  {
    "path": ".github/workflows/ios-prod.yml",
    "chars": 3040,
    "preview": "name: Release iOS Prod\n\non:\n    workflow_dispatch:\n    push:\n        branches:\n        - master\n\njobs:\n  build:\n    name"
  },
  {
    "path": ".github/workflows/ios-staging.yml",
    "chars": 3181,
    "preview": "name: Build iOS Staging\n\non:\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - '*'\n\njobs:\n  build:\n    name: Bu"
  },
  {
    "path": ".github/workflows/playwright.yml",
    "chars": 2231,
    "preview": "name: Playwright Tests\n\n# Only run one at a time per branch\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref "
  },
  {
    "path": ".gitignore",
    "chars": 411,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
  },
  {
    "path": "Dockerfile",
    "chars": 1445,
    "preview": "# Use Node.js as a base image for building the site\nFROM node:20-slim AS builder\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_H"
  },
  {
    "path": "LICENSE",
    "chars": 1080,
    "preview": "MIT License\n\nCopyright (c) 2022-2023 Mutiny Wallet Inc.\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "README.md",
    "chars": 5030,
    "preview": "### Running Mutiny Web\n\n### Dependencies\n\n-   pnpm > 8\n\n```\npnpm install\npnpm run dev\n```\n\n### Env\n\nThe easiest way to g"
  },
  {
    "path": "android/.gitignore",
    "chars": 1824,
    "preview": "# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore\n\n# Built application"
  },
  {
    "path": "android/app/.gitignore",
    "chars": 26,
    "preview": "/build/*\n!/build/.npmkeep\n"
  },
  {
    "path": "android/app/build.gradle",
    "chars": 2518,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    namespace \"com.mutinywallet.mutinywallet\"\n    compileSdk rootProj"
  },
  {
    "path": "android/app/capacitor.build.gradle",
    "chars": 942,
    "preview": "// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\n\nandroid {\n  compileOptions {\n      source"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
  },
  {
    "path": "android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java",
    "chars": 774,
    "preview": "package com.getcapacitor.myapp;\n\nimport static org.junit.Assert.*;\n\nimport android.content.Context;\nimport androidx.test"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "chars": 2700,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "android/app/src/main/java/com/mutinywallet/mutinywallet/MainActivity.java",
    "chars": 498,
    "preview": "package com.mutinywallet.mutinywallet;\n\nimport android.os.Bundle;\nimport android.webkit.WebView;\n\nimport com.getcapacito"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 4867,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n    android:height=\"108dp\"\n    android:width=\"108dp\"\n    android:viewport"
  },
  {
    "path": "android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1880,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    "
  },
  {
    "path": "android/app/src/main/res/layout/activity_main.xml",
    "chars": 535,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schema"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 268,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 268,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "android/app/src/main/res/values/ic_launcher_background.xml",
    "chars": 120,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "chars": 301,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<resources>\n    <string name=\"app_name\">Mutiny Wallet</string>\n    <string name=\""
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "chars": 823,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" pare"
  },
  {
    "path": "android/app/src/main/res/xml/file_paths.xml",
    "chars": 213,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <external-"
  },
  {
    "path": "android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java",
    "chars": 402,
    "preview": "package com.getcapacitor.myapp;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\n/**\n * Example local unit te"
  },
  {
    "path": "android/build.gradle",
    "chars": 636,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n"
  },
  {
    "path": "android/capacitor.settings.gradle",
    "chars": 2541,
    "preview": "// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME \"capacitor update\" IS RUN\ninclude ':capacitor-android'\nproject(':cap"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "chars": 223,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "android/gradle.properties",
    "chars": 987,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "android/gradlew",
    "chars": 8472,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "android/gradlew.bat",
    "chars": 2868,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "android/settings.gradle",
    "chars": 208,
    "preview": "include ':app'\ninclude ':capacitor-cordova-android-plugins'\nproject(':capacitor-cordova-android-plugins').projectDir = n"
  },
  {
    "path": "android/variables.gradle",
    "chars": 496,
    "preview": "ext {\n    minSdkVersion = 22\n    compileSdkVersion = 34\n    targetSdkVersion = 34\n    androidxActivityVersion = '1.8.0'\n"
  },
  {
    "path": "capacitor.config.ts",
    "chars": 295,
    "preview": "import { CapacitorConfig } from \"@capacitor/cli\";\n\nconst config: CapacitorConfig = {\n    appId: \"com.mutinywallet.mutiny"
  },
  {
    "path": "default.conf",
    "chars": 167,
    "preview": "server {\n    listen 80;\n\n    location / {\n        root   /usr/share/nginx/html;\n        index  index.html index.htm;\n   "
  },
  {
    "path": "e2e/encrypt.spec.ts",
    "chars": 3083,
    "preview": "import { expect, test } from \"@playwright/test\";\n\nimport { loadHome, visitSettings } from \"./utils\";\n\ntest.beforeEach(as"
  },
  {
    "path": "e2e/fedimint.spec.ts",
    "chars": 4501,
    "preview": "import { expect, test } from \"@playwright/test\";\n\nimport { loadHome } from \"./utils\";\n\nconst SIGNET_INVITE_CODE =\n    \"f"
  },
  {
    "path": "e2e/load.spec.ts",
    "chars": 242,
    "preview": "import { test } from \"@playwright/test\";\n\nimport { loadHome } from \"./utils\";\n\ntest.beforeEach(async ({ page }) => {\n   "
  },
  {
    "path": "e2e/restore.spec.ts",
    "chars": 2290,
    "preview": "import { expect, test } from \"@playwright/test\";\n\nimport { visitSettings } from \"./utils\";\n\ntest.beforeEach(async ({ pag"
  },
  {
    "path": "e2e/roundtrip.spec.ts",
    "chars": 4510,
    "preview": "import { expect, test } from \"@playwright/test\";\n\nimport { loadHome, visitSettings } from \"./utils\";\n\ntest.beforeEach(as"
  },
  {
    "path": "e2e/routes.spec.ts",
    "chars": 4191,
    "preview": "import { expect, Page, test } from \"@playwright/test\";\n\nimport { loadHome, visitSettings } from \"./utils\";\n\nconst routes"
  },
  {
    "path": "e2e/utils.ts",
    "chars": 1317,
    "preview": "import { expect, Page } from \"@playwright/test\";\n\nexport async function loadHome(page: Page) {\n    // Start on the home "
  },
  {
    "path": "flake.nix",
    "chars": 624,
    "preview": "{\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n    flake-utils.url = \"github:numtide/flake-ut"
  },
  {
    "path": "index.html",
    "chars": 4103,
    "preview": "<!doctype html>\n<html>\n    <head>\n        <title data-sm=\"\">Mutiny Wallet</title>\n        <meta charset=\"utf-8\" />\n     "
  },
  {
    "path": "ios/.gitignore",
    "chars": 239,
    "preview": "App/build\nApp/Pods\nApp/output\nApp/App/public\nDerivedData\nxcuserdata\n\n# Cordova plugins for Capacitor\ncapacitor-cordova-i"
  },
  {
    "path": "ios/App/App/App.entitlements",
    "chars": 307,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/App/App/AppDelegate.swift",
    "chars": 3338,
    "preview": "import UIKit\nimport Capacitor\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var windo"
  },
  {
    "path": "ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 2428,
    "preview": "{\n  \"images\": [\n    {\n      \"filename\": \"AppIcon-512@2x.png\",\n      \"idiom\": \"universal\",\n      \"platform\": \"ios\",\n     "
  },
  {
    "path": "ios/App/App/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ios/App/App/Assets.xcassets/Splash.imageset/Contents.json",
    "chars": 1156,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Default@1x~universal~anyany.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" "
  },
  {
    "path": "ios/App/App/Base.lproj/LaunchScreen.storyboard",
    "chars": 1994,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "ios/App/App/Base.lproj/Main.storyboard",
    "chars": 1071,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "ios/App/App/Info.plist",
    "chars": 1840,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/App/App.xcodeproj/project.pbxproj",
    "chars": 17765,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 148,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:App.xcodeproj\">"
  },
  {
    "path": "ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme",
    "chars": 3023,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1530\"\n   version = \"1.7\">\n   <BuildAction\n      "
  },
  {
    "path": "ios/App/App.xcworkspace/contents.xcworkspacedata",
    "chars": 221,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:App.xcodeproj\""
  },
  {
    "path": "ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/App/Gemfile",
    "chars": 46,
    "preview": "source \"https://rubygems.org\"\n\ngem \"fastlane\"\n"
  },
  {
    "path": "ios/App/MyCustomViewController.swift",
    "chars": 472,
    "preview": "//\n//  MyCustomViewController.swift\n//  App\n//\n//  Created by Paul Miller on 9/6/23.\n//\n\nimport UIKit\nimport Capacitor\n\n"
  },
  {
    "path": "ios/App/Podfile",
    "chars": 2452,
    "preview": "require_relative '../../node_modules/.pnpm/@capacitor+ios@6.0.0_@capacitor+core@6.0.0/node_modules/@capacitor/ios/script"
  },
  {
    "path": "ios/App/fastlane/.gitignore",
    "chars": 10,
    "preview": "report.xml"
  },
  {
    "path": "ios/App/fastlane/Appfile",
    "chars": 400,
    "preview": "app_identifier(ENV[\"DEVELOPER_APP_IDENTIFIER\"]) # The bundle identifier of your app\napple_id(ENV[\"FASTLANE_APPLE_ID\"]) #"
  },
  {
    "path": "ios/App/fastlane/Fastfile",
    "chars": 2547,
    "preview": "# This file contains the fastlane.tools configuration\n# You can find the documentation at https://docs.fastlane.tools\n#\n"
  },
  {
    "path": "ios/App/fastlane/Matchfile",
    "chars": 750,
    "preview": "CERTIFICATE_STORE_URL = ENV[\"CERTIFICATE_STORE_URL\"]\nGIT_USERNAME = ENV[\"GIT_USERNAME\"]\nGIT_TOKEN = ENV[\"GIT_TOKEN\"]\nFAS"
  },
  {
    "path": "ios/App/fastlane/README.md",
    "chars": 780,
    "preview": "fastlane documentation\n----\n\n# Installation\n\nMake sure you have the latest version of the Xcode command line tools insta"
  },
  {
    "path": "justfile",
    "chars": 453,
    "preview": "set dotenv-load := false\n\ndev:\n    pnpm run dev\n\npre:\n    pnpm run pre-commit\n\nlocal:\n    pnpm install && pnpm link --gl"
  },
  {
    "path": "manifest.ts",
    "chars": 13939,
    "preview": "import { ManifestOptions } from \"vite-plugin-pwa\";\n\nconst manifest: Partial<ManifestOptions> = {\n    name: \"Mutiny Walle"
  },
  {
    "path": "package.json",
    "chars": 2816,
    "preview": "{\n    \"name\": \"mutiny-wallet\",\n    \"version\": \"1.8.0\",\n    \"license\": \"MIT\",\n    \"packageManager\": \"pnpm@8.6.6\",\n    \"sc"
  },
  {
    "path": "playwright.config.ts",
    "chars": 2283,
    "preview": "import { defineConfig, devices } from \"@playwright/test\";\n\n/**\n * Read environment variables from file.\n * https://githu"
  },
  {
    "path": "postcss.config.cjs",
    "chars": 95,
    "preview": "module.exports = {\n    plugins: {\n      tailwindcss: {},\n      autoprefixer: {},\n    },\n  };\n  "
  },
  {
    "path": "prettier.config.mjs",
    "chars": 448,
    "preview": "/** @type {import(\"prettier\").Options} */\nexport default {\n    trailingComma: \"none\",\n    tabWidth: 4,\n    semi: true,\n "
  },
  {
    "path": "public/.well-known/apple-app-site-association",
    "chars": 156,
    "preview": "{\n  \"applinks\": {\n    \"apps\": [],\n    \"details\": [\n      {\n        \"appID\": \"X773Y823TN.com.mutinywallet.mutiny\",\n      "
  },
  {
    "path": "public/.well-known/assetlinks.json",
    "chars": 307,
    "preview": "[{\n  \"relation\": [\"delegate_permission/common.handle_all_urls\"],\n  \"target\": {\n    \"namespace\": \"android_app\",\n    \"pack"
  },
  {
    "path": "public/i18n/de.json",
    "chars": 35709,
    "preview": "{\n  \"common\": {\n    \"title\": \"Mutiny Wallet\",\n    \"mutiny\": \"Mutiny\",\n    \"nice\": \"Schön\",\n    \"home\": \"Home\",\n    \"e_sa"
  },
  {
    "path": "public/i18n/en.json",
    "chars": 38124,
    "preview": "{\n  \"common\": {\n    \"title\": \"Mutiny Wallet\",\n    \"mutiny\": \"Mutiny\",\n    \"nice\": \"Nice\",\n    \"home\": \"Home\",\n    \"e_sat"
  },
  {
    "path": "public/i18n/es.json",
    "chars": 34748,
    "preview": "{\n  \"common\": {\n    \"title\": \"Billetera Mutiny\",\n    \"mutiny\": \"Mutiny\",\n    \"nice\": \"Bien\",\n    \"home\": \"Inicio\",\n    \""
  },
  {
    "path": "public/i18n/fr.json",
    "chars": 41741,
    "preview": "{\n  \"common\": {\n    \"title\": \"Mutiny Wallet\",\n    \"mutiny\": \"Mutiny\",\n    \"nice\": \"C'est bien\",\n    \"home\": \"Accueil\",\n "
  },
  {
    "path": "public/i18n/hans.json",
    "chars": 29417,
    "preview": "{\n    \"common\": {\n        \"title\": \"Mutiny 钱包\",\n        \"mutiny\": \"Mutiny\",\n        \"nice\": \"不错\",\n        \"home\": \"首页\",\n"
  },
  {
    "path": "public/i18n/hant.json",
    "chars": 29407,
    "preview": "{\n    \"common\": {\n        \"title\": \"Mutiny 錢包\",\n        \"mutiny\": \"Mutiny\",\n        \"nice\": \"不錯\",\n        \"home\": \"首頁\",\n"
  },
  {
    "path": "public/i18n/it.json",
    "chars": 38277,
    "preview": "{\n  \"common\": {\n    \"title\": \"Mutiny Wallet\",\n    \"mutiny\": \"Mutiny\",\n    \"nice\": \"Figo\",\n    \"home\": \"Home\",\n    \"e_sat"
  },
  {
    "path": "public/i18n/ko.json",
    "chars": 15616,
    "preview": "{\n  \"common\": {\n    \"title\": \"Mutiny 지갑\",\n    \"nice\": \"멋지다\",\n    \"home\": \"홈\",\n    \"sats\": \"SATS\",\n    \"sat\": \"SAT\",\n    "
  },
  {
    "path": "public/i18n/pt.json",
    "chars": 1201,
    "preview": "{\n  \"create_an_issue\": \"Crie uma issue\",\n  \"view_all\": \"Ver todas\",\n  \"receive_some_sats_to_get_started\": \"Receba alguns"
  },
  {
    "path": "public/robots.txt",
    "chars": 23,
    "preview": "User-agent: *\nAllow: /\n"
  },
  {
    "path": "scripts/errorsToTs.cjs",
    "chars": 934,
    "preview": "const fs = require(\"fs\").promises;\n\n(async () => {\n    const filePath = process.argv[2]; // grab the file path from the "
  },
  {
    "path": "src/components/Activity.tsx",
    "chars": 15855,
    "preview": "import { TagItem } from \"@mutinywallet/mutiny-wasm\";\nimport { cache, createAsync, useNavigate } from \"@solidjs/router\";\n"
  },
  {
    "path": "src/components/ActivityDetailsModal.tsx",
    "chars": 25618,
    "preview": "import { Dialog } from \"@kobalte/core\";\nimport {\n    ActivityItem,\n    MutinyInvoice,\n    TagItem\n} from \"@mutinywallet/"
  },
  {
    "path": "src/components/Amount.tsx",
    "chars": 4113,
    "preview": "import { createAsync } from \"@solidjs/router\";\nimport { Link, Users, Zap } from \"lucide-solid\";\nimport { Show } from \"so"
  },
  {
    "path": "src/components/AmountEditable.tsx",
    "chars": 9579,
    "preview": "import {\n    createEffect,\n    createSignal,\n    onCleanup,\n    onMount,\n    ParentComponent,\n    Show\n} from \"solid-js\""
  },
  {
    "path": "src/components/BalanceBox.tsx",
    "chars": 9093,
    "preview": "import { A, useNavigate } from \"@solidjs/router\";\nimport { Shuffle, Users } from \"lucide-solid\";\nimport { createMemo, Ma"
  },
  {
    "path": "src/components/BigMoney.tsx",
    "chars": 3916,
    "preview": "import { ArrowDownUp } from \"lucide-solid\";\nimport { Show } from \"solid-js\";\n\nimport { useI18n } from \"~/i18n/context\";\n"
  },
  {
    "path": "src/components/ChooseCurrency.tsx",
    "chars": 3763,
    "preview": "import { createForm } from \"@modular-forms/solid\";\nimport { useNavigate } from \"@solidjs/router\";\nimport { createSignal,"
  },
  {
    "path": "src/components/ChooseLanguage.tsx",
    "chars": 3626,
    "preview": "import { createForm } from \"@modular-forms/solid\";\nimport { useNavigate } from \"@solidjs/router\";\nimport { createSignal,"
  },
  {
    "path": "src/components/ContactButton.tsx",
    "chars": 1022,
    "preview": "import { TagItem } from \"@mutinywallet/mutiny-wasm\";\n\nimport { LabelCircle } from \"~/components\";\nimport { PseudoContact"
  },
  {
    "path": "src/components/ContactEditor.tsx",
    "chars": 2279,
    "preview": "import { SubmitHandler } from \"@modular-forms/solid\";\nimport { createSignal, Match, Switch } from \"solid-js\";\n\nimport {\n"
  },
  {
    "path": "src/components/ContactForm.tsx",
    "chars": 3640,
    "preview": "import {\n    createForm,\n    custom,\n    email,\n    required,\n    SubmitHandler\n} from \"@modular-forms/solid\";\n\nimport {"
  },
  {
    "path": "src/components/ContactViewer.tsx",
    "chars": 7403,
    "preview": "import { SubmitHandler } from \"@modular-forms/solid\";\nimport { TagItem } from \"@mutinywallet/mutiny-wasm\";\nimport { useN"
  },
  {
    "path": "src/components/DecryptDialog.tsx",
    "chars": 2562,
    "preview": "import { A } from \"@solidjs/router\";\nimport { createSignal, Show } from \"solid-js\";\n\nimport { Button, InfoBox, SimpleDia"
  },
  {
    "path": "src/components/DeleteEverything.tsx",
    "chars": 2544,
    "preview": "import { SecureStoragePlugin } from \"capacitor-secure-storage-plugin\";\nimport { createSignal } from \"solid-js\";\n\nimport "
  },
  {
    "path": "src/components/EditProfileForm.tsx",
    "chars": 5471,
    "preview": "import { createForm, email } from \"@modular-forms/solid\";\nimport { createFileUploader } from \"@solid-primitives/upload\";"
  },
  {
    "path": "src/components/ErrorDisplay.tsx",
    "chars": 1900,
    "preview": "import { Title } from \"@solidjs/meta\";\nimport { A } from \"@solidjs/router\";\nimport { onMount } from \"solid-js\";\n\nimport "
  },
  {
    "path": "src/components/Fab.tsx",
    "chars": 5677,
    "preview": "import { useNavigate } from \"@solidjs/router\";\nimport { ArrowDownLeft, ArrowUpRight, Plus, Scan } from \"lucide-solid\";\ni"
  },
  {
    "path": "src/components/Failure.tsx",
    "chars": 1521,
    "preview": "import { A } from \"@solidjs/router\";\nimport { Match, Switch } from \"solid-js\";\n\nimport { InfoBox, MegaClock, MegaEx } fr"
  },
  {
    "path": "src/components/FederationInviteShower.tsx",
    "chars": 1838,
    "preview": "import { Copy, QrCode } from \"lucide-solid\";\nimport { createSignal, Show } from \"solid-js\";\nimport { QRCodeSVG } from \"s"
  },
  {
    "path": "src/components/FederationPopup.tsx",
    "chars": 1623,
    "preview": "import { useNavigate } from \"@solidjs/router\";\nimport { Users } from \"lucide-solid\";\nimport { createSignal } from \"solid"
  },
  {
    "path": "src/components/Fee.tsx",
    "chars": 956,
    "preview": "import { AmountFiat, AmountSats, FeesModal } from \"~/components\";\nimport { useI18n } from \"~/i18n/context\";\n\nexport func"
  },
  {
    "path": "src/components/FeeDisplay.tsx",
    "chars": 3487,
    "preview": "import { createAsync } from \"@solidjs/router\";\nimport { createMemo, ParentComponent, Show, Suspense } from \"solid-js\";\n\n"
  },
  {
    "path": "src/components/GenericItem.tsx",
    "chars": 8612,
    "preview": "import { Check, Clock4, EyeOff, Globe, X, Zap } from \"lucide-solid\";\nimport { JSX, Match, Show, Switch } from \"solid-js\""
  },
  {
    "path": "src/components/HomeBalance.tsx",
    "chars": 2063,
    "preview": "import { createMemo, Match, Suspense, Switch } from \"solid-js\";\n\nimport { AmountFiat, AmountSats, LoadingShimmer } from "
  },
  {
    "path": "src/components/HomePrompt.tsx",
    "chars": 5594,
    "preview": "import { useSearchParams } from \"@solidjs/router\";\nimport { createEffect, createSignal, onMount, Show } from \"solid-js\";"
  },
  {
    "path": "src/components/HomeSubnav.tsx",
    "chars": 4243,
    "preview": "import { useSearchParams } from \"@solidjs/router\";\nimport {\n    createEffect,\n    createResource,\n    createSignal,\n    "
  },
  {
    "path": "src/components/I18nProvider.tsx",
    "chars": 699,
    "preview": "import i18next from \"i18next\";\nimport { createResource, ParentComponent, Show } from \"solid-js\";\n\nimport i18nConfig from"
  },
  {
    "path": "src/components/IOSbanner.tsx",
    "chars": 1889,
    "preview": "import { X } from \"lucide-solid\";\nimport { Show } from \"solid-js\";\n\nimport { ButtonLink } from \"~/components\";\nimport { "
  },
  {
    "path": "src/components/ImportExport.tsx",
    "chars": 7420,
    "preview": "import { createFileUploader } from \"@solid-primitives/upload\";\nimport { createSignal, Show } from \"solid-js\";\n\nimport {\n"
  },
  {
    "path": "src/components/ImportNsecForm.tsx",
    "chars": 1893,
    "preview": "import { useNavigate } from \"@solidjs/router\";\nimport { SecureStoragePlugin } from \"capacitor-secure-storage-plugin\";\nim"
  },
  {
    "path": "src/components/InfoBox.tsx",
    "chars": 877,
    "preview": "import { Info } from \"lucide-solid\";\nimport { ParentComponent } from \"solid-js\";\n\nexport const InfoBox: ParentComponent<"
  },
  {
    "path": "src/components/IntegratedQR.tsx",
    "chars": 5079,
    "preview": "import { Copy, Link, Share, Zap } from \"lucide-solid\";\nimport { Match, Show, Switch } from \"solid-js\";\nimport { QRCodeSV"
  },
  {
    "path": "src/components/JsonModal.tsx",
    "chars": 1015,
    "preview": "import { createMemo, JSX } from \"solid-js\";\n\nimport { CopyButton, SimpleDialog } from \"~/components\";\nimport { useI18n }"
  },
  {
    "path": "src/components/KitchenSink.tsx",
    "chars": 19991,
    "preview": "import { Collapsible, TextField } from \"@kobalte/core\";\nimport { createForm, url } from \"@modular-forms/solid\";\nimport {"
  },
  {
    "path": "src/components/LabelCircle.tsx",
    "chars": 3254,
    "preview": "import { createResource, createSignal, JSX, Match, Switch } from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\n\nim"
  },
  {
    "path": "src/components/LightningAddressShower.tsx",
    "chars": 3524,
    "preview": "import { Copy, QrCode } from \"lucide-solid\";\nimport { createMemo, createSignal, Match, Show, Switch } from \"solid-js\";\ni"
  },
  {
    "path": "src/components/LoadingIndicator.tsx",
    "chars": 2245,
    "preview": "import { Progress } from \"@kobalte/core\";\nimport { Show } from \"solid-js\";\n\nimport { useI18n } from \"~/i18n/context\";\nim"
  },
  {
    "path": "src/components/Logo.tsx",
    "chars": 849,
    "preview": "import { Match, Switch } from \"solid-js\";\n\nimport pixelLogo from \"~/assets/mutiny-pixel-logo.png\";\nimport plusLogo from "
  },
  {
    "path": "src/components/Logs.tsx",
    "chars": 1432,
    "preview": "import { createSignal, Show } from \"solid-js\";\n\nimport { Button, InfoBox, InnerCard, NiceP, VStack } from \"~/components\""
  },
  {
    "path": "src/components/MoreInfoModal.tsx",
    "chars": 1446,
    "preview": "import { createSignal, JSXElement, ParentComponent } from \"solid-js\";\n\nimport { ExternalLink, SimpleDialog } from \"~/com"
  },
  {
    "path": "src/components/MutinyPlusCta.tsx",
    "chars": 1724,
    "preview": "import { A } from \"@solidjs/router\";\nimport { ChevronRight } from \"lucide-solid\";\nimport { ParentComponent, Show } from "
  },
  {
    "path": "src/components/NWCEditor.tsx",
    "chars": 20293,
    "preview": "import {\n    createForm,\n    getValue,\n    required,\n    setValue,\n    SubmitHandler\n} from \"@modular-forms/solid\";\nimpo"
  },
  {
    "path": "src/components/NavBar.tsx",
    "chars": 2267,
    "preview": "import { A } from \"@solidjs/router\";\nimport {\n    ArrowDownLeft,\n    ArrowUpRight,\n    Scan,\n    Settings,\n    User,\n   "
  },
  {
    "path": "src/components/NostrActivity.tsx",
    "chars": 6234,
    "preview": "import { createAsync, useNavigate } from \"@solidjs/router\";\nimport { Search } from \"lucide-solid\";\nimport {\n    createEf"
  },
  {
    "path": "src/components/PendingNwc.tsx",
    "chars": 7560,
    "preview": "import { useNavigate } from \"@solidjs/router\";\nimport { Check, PlugZap, X } from \"lucide-solid\";\nimport {\n    createEffe"
  },
  {
    "path": "src/components/Reader.tsx",
    "chars": 3630,
    "preview": "import {\n    BarcodeFormat,\n    BarcodeScannedEvent,\n    BarcodeScanner,\n    PermissionStatus\n} from \"@capacitor-mlkit/b"
  },
  {
    "path": "src/components/ReceiveWarnings.tsx",
    "chars": 3282,
    "preview": "import { createMemo, createResource, Match, Switch } from \"solid-js\";\n\nimport { InfoBox } from \"~/components/InfoBox\";\ni"
  },
  {
    "path": "src/components/Reload.tsx",
    "chars": 2427,
    "preview": "import { RotateCw, X } from \"lucide-solid\";\nimport { Show } from \"solid-js\";\n// eslint-disable-next-line import/no-unres"
  },
  {
    "path": "src/components/Restart.tsx",
    "chars": 1171,
    "preview": "import { createSignal } from \"solid-js\";\n\nimport { Button, InnerCard, NiceP, VStack } from \"~/components\";\nimport { useI"
  },
  {
    "path": "src/components/ResyncOnchain.tsx",
    "chars": 749,
    "preview": "import { Button, InnerCard, NiceP, VStack } from \"~/components\";\nimport { useI18n } from \"~/i18n/context\";\nimport { useM"
  },
  {
    "path": "src/components/SeedWords.tsx",
    "chars": 3289,
    "preview": "import { Copy } from \"lucide-solid\";\nimport { createMemo, createSignal, For, Match, Switch } from \"solid-js\";\n\nimport { "
  },
  {
    "path": "src/components/SetupErrorDisplay.tsx",
    "chars": 9775,
    "preview": "import { Title } from \"@solidjs/meta\";\nimport { MonitorSmartphone } from \"lucide-solid\";\nimport { createResource, Match,"
  },
  {
    "path": "src/components/ShareCard.tsx",
    "chars": 3235,
    "preview": "import { Copy, Eye, Share } from \"lucide-solid\";\nimport { createSignal, Show } from \"solid-js\";\n\nimport { Card, JsonModa"
  },
  {
    "path": "src/components/SharpButton.tsx",
    "chars": 634,
    "preview": "import { JSX } from \"solid-js\";\n\nexport function SharpButton(props: {\n    onClick: () => void;\n    children: JSX.Element"
  },
  {
    "path": "src/components/ShutdownPopup.tsx",
    "chars": 1184,
    "preview": "import { createSignal } from \"solid-js\";\n\nimport { ExternalLink, NiceP, SimpleDialog } from \"~/components/layout\";\nimpor"
  },
  {
    "path": "src/components/SimpleInput.tsx",
    "chars": 685,
    "preview": "import { JSX } from \"solid-js\";\n\ntype SimpleInputProps = {\n    type?: \"text\" | \"email\" | \"tel\" | \"password\" | \"url\" | \"d"
  },
  {
    "path": "src/components/SocialActionRow.tsx",
    "chars": 3145,
    "preview": "import { TagItem } from \"@mutinywallet/mutiny-wasm\";\nimport { cache, createAsync, useNavigate } from \"@solidjs/router\";\n"
  },
  {
    "path": "src/components/Toaster.tsx",
    "chars": 2153,
    "preview": "import { Toast, toaster } from \"@kobalte/core\";\nimport { X } from \"lucide-solid\";\nimport { Portal } from \"solid-js/web\";"
  },
  {
    "path": "src/components/ToggleHodl.tsx",
    "chars": 1280,
    "preview": "import { Button, InnerCard, NiceP, VStack } from \"~/components\";\nimport { useI18n } from \"~/i18n/context\";\nimport { useM"
  },
  {
    "path": "src/components/index.ts",
    "chars": 1844,
    "preview": "export * from \"./layout\";\nexport * from \"./successfail\";\n\nexport * from \"./Activity\";\nexport * from \"./ActivityDetailsMo"
  },
  {
    "path": "src/components/layout/BackLink.tsx",
    "chars": 1277,
    "preview": "import { A } from \"@solidjs/router\";\nimport { ChevronLeft } from \"lucide-solid\";\n\nimport { useI18n } from \"~/i18n/contex"
  },
  {
    "path": "src/components/layout/BackPop.tsx",
    "chars": 2295,
    "preview": "import { useLocation, useNavigate } from \"@solidjs/router\";\nimport { JSXElement } from \"solid-js\";\n\nimport { BackButton "
  },
  {
    "path": "src/components/layout/Button.tsx",
    "chars": 4955,
    "preview": "import { A } from \"@solidjs/router\";\nimport { JSX, ParentComponent, Show, splitProps } from \"solid-js\";\nimport { Dynamic"
  },
  {
    "path": "src/components/layout/ExternalLink.tsx",
    "chars": 691,
    "preview": "import { ParentComponent } from \"solid-js\";\n\nexport const ExternalLink: ParentComponent<{ href: string }> = (props) => {"
  },
  {
    "path": "src/components/layout/LoadingSpinner.tsx",
    "chars": 1912,
    "preview": "export const LoadingSpinner = (props: {\n    big?: boolean;\n    wide?: boolean;\n    small?: boolean;\n    smallest?: boole"
  },
  {
    "path": "src/components/layout/Misc.tsx",
    "chars": 14033,
    "preview": "import {\n    Collapsible,\n    Dialog,\n    Checkbox as KCheckbox,\n    Separator\n} from \"@kobalte/core\";\nimport { TagItem,"
  },
  {
    "path": "src/components/layout/Radio.tsx",
    "chars": 4280,
    "preview": "import { RadioGroup } from \"@kobalte/core\";\nimport { createSignal, For, Show } from \"solid-js\";\n\nimport { timeout } from"
  },
  {
    "path": "src/components/layout/SubtleButton.tsx",
    "chars": 1280,
    "preview": "import { JSX, Show } from \"solid-js\";\n\nimport { LoadingSpinner } from \"./LoadingSpinner\";\n\nexport function SubtleButton("
  },
  {
    "path": "src/components/layout/TextField.tsx",
    "chars": 2689,
    "preview": "import { TextField as KTextField } from \"@kobalte/core\";\nimport { Show, splitProps, type JSX } from \"solid-js\";\n\nimport "
  },
  {
    "path": "src/components/layout/index.ts",
    "chars": 257,
    "preview": "export * from \"./BackLink\";\nexport * from \"./BackPop\";\nexport * from \"./Button\";\nexport * from \"./Misc\";\nexport * from \""
  },
  {
    "path": "src/components/successfail/MegaCheck.tsx",
    "chars": 244,
    "preview": "import megacheck from \"~/assets/icons/megacheck.png\";\n\nexport function MegaCheck() {\n    return (\n        <img\n         "
  },
  {
    "path": "src/components/successfail/MegaClock.tsx",
    "chars": 241,
    "preview": "import megaclock from \"~/assets/icons/megaclock.png\";\n\nexport function MegaClock() {\n    return (\n        <img\n         "
  },
  {
    "path": "src/components/successfail/MegaEx.tsx",
    "chars": 229,
    "preview": "import megaex from \"~/assets/icons/megaex.png\";\n\nexport function MegaEx() {\n    return (\n        <img\n            src={m"
  },
  {
    "path": "src/components/successfail/SuccessModal.tsx",
    "chars": 1527,
    "preview": "import { Dialog } from \"@kobalte/core\";\nimport { JSX } from \"solid-js\";\n\nimport { Button } from \"~/components\";\nimport {"
  },
  {
    "path": "src/components/successfail/index.ts",
    "chars": 116,
    "preview": "export * from \"./MegaCheck\";\nexport * from \"./MegaEx\";\nexport * from \"./MegaClock\";\nexport * from \"./SuccessModal\";\n"
  },
  {
    "path": "src/i18n/config.ts",
    "chars": 982,
    "preview": "import { use } from \"i18next\";\nimport LanguageDetector from \"i18next-browser-languagedetector\";\nimport HttpApi from \"i18"
  },
  {
    "path": "src/i18n/context.ts",
    "chars": 294,
    "preview": "import { i18n } from \"i18next\";\nimport { createContext, useContext } from \"solid-js\";\n\nexport const I18nContext = create"
  },
  {
    "path": "src/index.tsx",
    "chars": 178,
    "preview": "import { render } from \"solid-js/web\";\n\nimport \"./root.css\";\n\nimport { Router } from \"./router\";\n\nconst root = document."
  },
  {
    "path": "src/logic/browserCompatibility.ts",
    "chars": 1642,
    "preview": "export async function checkBrowserCompatibility(): Promise<boolean> {\n    // Check if we can write to localstorage\n    c"
  },
  {
    "path": "src/logic/errorDispatch.ts",
    "chars": 3252,
    "preview": "// IMPORTANT: this should match 1:1 with the MutinyJsError enum in mutiny-wasm\n// If we can handle all of these, we can "
  },
  {
    "path": "src/logic/mutinyWalletSetup.ts",
    "chars": 5942,
    "preview": "export type Network = \"bitcoin\" | \"testnet\" | \"regtest\" | \"signet\";\n\nexport type MutinyWalletSettingStrings = {\n    netw"
  },
  {
    "path": "src/logic/waila.ts",
    "chars": 1968,
    "preview": "import { WalletWorker } from \"~/state/megaStore\";\nimport { Result } from \"~/utils\";\n\nexport type ParsedParams = {\n    or"
  },
  {
    "path": "src/root.css",
    "chars": 2863,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n* {\n    touch-action: manipulation;\n    user-select: none;\n}"
  },
  {
    "path": "src/router.tsx",
    "chars": 7416,
    "preview": "import { App as CapacitorApp } from \"@capacitor/app\";\nimport { Capacitor } from \"@capacitor/core\";\nimport { StatusBar, S"
  },
  {
    "path": "src/routes/Chat.tsx",
    "chars": 24760,
    "preview": "import { TagItem } from \"@mutinywallet/mutiny-wasm\";\nimport { createAsync, useNavigate, useParams } from \"@solidjs/route"
  },
  {
    "path": "src/routes/EditProfile.tsx",
    "chars": 2487,
    "preview": "import { createAsync, useNavigate } from \"@solidjs/router\";\nimport { createSignal, Show } from \"solid-js\";\n\nimport {\n   "
  },
  {
    "path": "src/routes/Feedback.tsx",
    "chars": 6974,
    "preview": "import { createForm, required, SubmitHandler } from \"@modular-forms/solid\";\nimport { A, useLocation } from \"@solidjs/rou"
  },
  {
    "path": "src/routes/Main.tsx",
    "chars": 3130,
    "preview": "import { createAsync, useNavigate } from \"@solidjs/router\";\nimport { Show, Suspense } from \"solid-js\";\n\nimport {\n    Cir"
  },
  {
    "path": "src/routes/Profile.tsx",
    "chars": 3396,
    "preview": "import { createAsync, useNavigate } from \"@solidjs/router\";\nimport { Edit, Import } from \"lucide-solid\";\nimport { create"
  },
  {
    "path": "src/routes/Receive.tsx",
    "chars": 20293,
    "preview": "import { MutinyInvoice } from \"@mutinywallet/mutiny-wasm\";\nimport { useNavigate } from \"@solidjs/router\";\nimport { Circl"
  },
  {
    "path": "src/routes/Redeem.tsx",
    "chars": 8887,
    "preview": "import { LnUrlParams } from \"@mutinywallet/mutiny-wasm\";\nimport { useNavigate } from \"@solidjs/router\";\nimport {\n    cre"
  },
  {
    "path": "src/routes/Request.tsx",
    "chars": 4522,
    "preview": "import { useNavigate, useParams } from \"@solidjs/router\";\nimport { createResource, createSignal, Suspense } from \"solid-"
  },
  {
    "path": "src/routes/Scanner.tsx",
    "chars": 2256,
    "preview": "import { Clipboard } from \"@capacitor/clipboard\";\nimport { Capacitor } from \"@capacitor/core\";\nimport { useNavigate } fr"
  },
  {
    "path": "src/routes/Search.tsx",
    "chars": 16905,
    "preview": "import { Clipboard } from \"@capacitor/clipboard\";\nimport { Capacitor } from \"@capacitor/core\";\nimport { TagItem } from \""
  },
  {
    "path": "src/routes/Send.tsx",
    "chars": 35592,
    "preview": "import { MutinyInvoice, TagItem } from \"@mutinywallet/mutiny-wasm\";\nimport { useLocation, useNavigate, useSearchParams }"
  },
  {
    "path": "src/routes/Swap.tsx",
    "chars": 17958,
    "preview": "import { createForm, required } from \"@modular-forms/solid\";\nimport { MutinyChannel } from \"@mutinywallet/mutiny-wasm\";\n"
  },
  {
    "path": "src/routes/SwapLightning.tsx",
    "chars": 12805,
    "preview": "import { FedimintSweepResult } from \"@mutinywallet/mutiny-wasm\";\nimport { useNavigate } from \"@solidjs/router\";\nimport {"
  },
  {
    "path": "src/routes/Transfer.tsx",
    "chars": 8274,
    "preview": "import { FedimintSweepResult } from \"@mutinywallet/mutiny-wasm\";\nimport { createAsync, useNavigate, useSearchParams } fr"
  },
  {
    "path": "src/routes/[...404].tsx",
    "chars": 616,
    "preview": "import { Title } from \"@solidjs/meta\";\n\nimport { ButtonLink, DefaultMain, LargeHeader } from \"~/components\";\nimport { us"
  },
  {
    "path": "src/routes/index.ts",
    "chars": 404,
    "preview": "export * from \"./[...404]\";\nexport * from \"./Feedback\";\nexport * from \"./Main\";\nexport * from \"./Receive\";\nexport * from"
  },
  {
    "path": "src/routes/settings/Admin.tsx",
    "chars": 1164,
    "preview": "import {\n    BackLink,\n    DefaultMain,\n    DeleteEverything,\n    KitchenSink,\n    LargeHeader,\n    MutinyWalletGuard,\n "
  },
  {
    "path": "src/routes/settings/Backup.tsx",
    "chars": 3267,
    "preview": "import { createAsync, useNavigate } from \"@solidjs/router\";\nimport { createEffect, createSignal, Show } from \"solid-js\";"
  },
  {
    "path": "src/routes/settings/Channels.tsx",
    "chars": 10890,
    "preview": "import { MutinyChannel } from \"@mutinywallet/mutiny-wasm\";\nimport {\n    createEffect,\n    createMemo,\n    createResource"
  },
  {
    "path": "src/routes/settings/Connections.tsx",
    "chars": 12322,
    "preview": "import { NwcProfile } from \"@mutinywallet/mutiny-wasm\";\nimport { A, useSearchParams } from \"@solidjs/router\";\nimport { S"
  },
  {
    "path": "src/routes/settings/Currency.tsx",
    "chars": 756,
    "preview": "import {\n    BackLink,\n    Card,\n    ChooseCurrency,\n    DefaultMain,\n    LargeHeader,\n    MutinyWalletGuard,\n    NavBar"
  },
  {
    "path": "src/routes/settings/EmergencyKit.tsx",
    "chars": 2053,
    "preview": "import {\n    BackLink,\n    Button,\n    DefaultMain,\n    DeleteEverything,\n    ExternalLink,\n    ImportExport,\n    InnerC"
  },
  {
    "path": "src/routes/settings/Encrypt.tsx",
    "chars": 6685,
    "preview": "import { createForm } from \"@modular-forms/solid\";\nimport { createMemo, createSignal, Show } from \"solid-js\";\n\nimport {\n"
  },
  {
    "path": "src/routes/settings/ImportProfile.tsx",
    "chars": 911,
    "preview": "import {\n    BackLink,\n    DefaultMain,\n    ImportNsecForm,\n    MutinyWalletGuard\n} from \"~/components\";\n\nexport functio"
  }
]

// ... and 50 more files (download for full content)

About this extraction

This page contains the full source code of the MutinyWallet/mutiny-web GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 250 files (18.8 MB), approximately 285.7k tokens, and a symbol index with 504 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!