├── .gitattributes ├── .gitignore ├── .readme └── phoenix_text.png ├── BUILD.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── Versions.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties.template ├── phoenix-android ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── fr │ │ └── acinq │ │ └── phoenix │ │ └── android │ │ ├── Ambients.kt │ │ ├── AppView.kt │ │ ├── AppViewModel.kt │ │ ├── MainActivity.kt │ │ ├── Navigation.kt │ │ ├── PhoenixApplication.kt │ │ ├── components │ │ ├── AmountView.kt │ │ ├── Buttons.kt │ │ ├── Inputs.kt │ │ ├── Layout.kt │ │ └── mvi │ │ │ ├── MVIControllerViewModel.kt │ │ │ ├── MVIView.kt │ │ │ └── Mocks.kt │ │ ├── home │ │ ├── HomeView.kt │ │ ├── ReadDataView.kt │ │ └── StartupView.kt │ │ ├── init │ │ └── InitView.kt │ │ ├── receive │ │ └── ReceiveView.kt │ │ ├── security │ │ ├── EncryptedSeed.kt │ │ ├── KeystoreHelper.kt │ │ └── SeedManager.kt │ │ ├── send │ │ └── SendView.kt │ │ ├── settings │ │ ├── ChannelsView.kt │ │ ├── ElectrumView.kt │ │ ├── MutualClose.kt │ │ ├── SeedView.kt │ │ └── SettingsView.kt │ │ └── utils │ │ ├── Clipboard.kt │ │ ├── Converter.kt │ │ ├── Logging.kt │ │ ├── Prefs.kt │ │ ├── QRCode.kt │ │ ├── Theme.kt │ │ ├── extensions.kt │ │ └── logger.kt │ └── res │ ├── drawable │ ├── ic_alert_triangle.xml │ ├── ic_arrow_back.xml │ ├── ic_arrow_next.xml │ ├── ic_blank.xml │ ├── ic_brush.xml │ ├── ic_chain.xml │ ├── ic_check_circle.xml │ ├── ic_chevron_down.xml │ ├── ic_clipboard.xml │ ├── ic_connection_lost.xml │ ├── ic_copy.xml │ ├── ic_cross_circle.xml │ ├── ic_edit.xml │ ├── ic_fire.xml │ ├── ic_help_circle.xml │ ├── ic_info.xml │ ├── ic_key.xml │ ├── ic_launcher_foreground.xml │ ├── ic_lock.xml │ ├── ic_notification.xml │ ├── ic_payment_failed.xml │ ├── ic_payment_pending.xml │ ├── ic_payment_success.xml │ ├── ic_payment_success_onchain.xml │ ├── ic_qrcode.xml │ ├── ic_receive.xml │ ├── ic_restore.xml │ ├── ic_scan.xml │ ├── ic_send.xml │ ├── ic_settings.xml │ ├── ic_share.xml │ ├── ic_shield.xml │ ├── ic_swap.xml │ ├── ic_text.xml │ ├── ic_tool.xml │ ├── ic_tor_shield.xml │ ├── ic_unlock.xml │ ├── ic_user.xml │ ├── ic_zap.xml │ ├── illus_phoenix.xml │ ├── illus_send.xml │ └── line_dots.xml │ ├── layout │ ├── custom_barcode_scanner.xml │ └── scan_view.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-night │ └── themes.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── themes.xml ├── phoenix-ios ├── phoenix-ios-framework │ └── Info.plist ├── phoenix-ios.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ ├── phoenix-ios-framework.xcscheme │ │ └── phoenix-ios.xcscheme ├── phoenix-ios │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon-Blue.appiconset │ │ │ ├── Contents.json │ │ │ ├── logo_1024.png │ │ │ ├── logo_120-1.png │ │ │ ├── logo_120.png │ │ │ ├── logo_152.png │ │ │ ├── logo_167.png │ │ │ ├── logo_180.png │ │ │ ├── logo_20.png │ │ │ ├── logo_29.png │ │ │ ├── logo_40-1.png │ │ │ ├── logo_40-2.png │ │ │ ├── logo_40.png │ │ │ ├── logo_58-1.png │ │ │ ├── logo_58.png │ │ │ ├── logo_60.png │ │ │ ├── logo_76.png │ │ │ ├── logo_80-1.png │ │ │ ├── logo_80.png │ │ │ └── logo_87.png │ │ ├── AppIcon-Green.appiconset │ │ │ ├── 1024.png │ │ │ ├── 120-1.png │ │ │ ├── 120.png │ │ │ ├── 180.png │ │ │ ├── 40.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 80.png │ │ │ ├── 87.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── ic_arrow_back.imageset │ │ │ ├── Contents.json │ │ │ └── ic_arrow_back.pdf │ │ ├── ic_arrow_next.imageset │ │ │ ├── Contents.json │ │ │ └── ic_arrow_next.pdf │ │ ├── ic_bullet.imageset │ │ │ ├── Contents.json │ │ │ └── ic_bullet.pdf │ │ ├── ic_check.imageset │ │ │ ├── Contents.json │ │ │ └── ic_check.pdf │ │ ├── ic_check_circle.imageset │ │ │ ├── Contents.json │ │ │ └── ic_check_circle.pdf │ │ ├── ic_connection_lost.imageset │ │ │ ├── Contents.json │ │ │ └── ic_connection_lost.pdf │ │ ├── ic_cross.imageset │ │ │ ├── Contents.json │ │ │ └── ic_cross.pdf │ │ ├── ic_edit.imageset │ │ │ ├── Contents.json │ │ │ └── ic_edit.pdf │ │ ├── ic_fire.imageset │ │ │ ├── Contents.json │ │ │ └── ic_fire.pdf │ │ ├── ic_payment_sending.imageset │ │ │ ├── Contents.json │ │ │ └── ic_payment_sending.svg │ │ ├── ic_payment_sent.imageset │ │ │ ├── Contents.json │ │ │ └── ic_payment_sent.svg │ │ ├── ic_payment_success_static.imageset │ │ │ ├── Contents.json │ │ │ └── ic_payment_success_static.pdf │ │ ├── ic_receive.imageset │ │ │ ├── Contents.json │ │ │ └── ic_receive.pdf │ │ ├── ic_restore.imageset │ │ │ ├── Contents.json │ │ │ └── ic_restore.pdf │ │ ├── ic_scan.imageset │ │ │ ├── Contents.json │ │ │ └── ic_scan.pdf │ │ ├── ic_send.imageset │ │ │ ├── Contents.json │ │ │ └── ic_send.pdf │ │ ├── ic_settings.imageset │ │ │ ├── Contents.json │ │ │ └── ic_settings.pdf │ │ ├── logo_blue.imageset │ │ │ ├── Contents.json │ │ │ └── logo.svg │ │ ├── logo_blue_192.imageset │ │ │ ├── Contents.json │ │ │ ├── logo_192.png │ │ │ └── logo_192@2x.png │ │ ├── logo_green.imageset │ │ │ ├── Contents.json │ │ │ └── phoenix.svg │ │ ├── logo_green_192.imageset │ │ │ ├── Contents.json │ │ │ ├── logo_green_192.png │ │ │ └── logo_green_384.png │ │ ├── payment_holder_def_failed.imageset │ │ │ ├── Contents.json │ │ │ └── payment_holder_def_failed.pdf │ │ ├── payment_holder_def_pending.imageset │ │ │ ├── Contents.json │ │ │ └── payment_holder_def_pending.pdf │ │ ├── payment_holder_def_success.imageset │ │ │ ├── Contents.json │ │ │ └── payment_holder_def_success.pdf │ │ └── testnet_bg.imageset │ │ │ ├── Contents.json │ │ │ ├── testnet_dark_path.svg │ │ │ └── testnet_light_path.svg │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Colors.xcassets │ │ ├── Contents.json │ │ ├── appAccentBlue.colorset │ │ │ └── Contents.json │ │ ├── appAccentGreen.colorset │ │ │ └── Contents.json │ │ ├── appNegative.colorset │ │ │ └── Contents.json │ │ ├── appWarn.colorset │ │ │ └── Contents.json │ │ ├── borderColor.colorset │ │ │ └── Contents.json │ │ ├── buttonFill.colorset │ │ │ └── Contents.json │ │ ├── mutedBackground.colorset │ │ │ └── Contents.json │ │ ├── primaryBackground.colorset │ │ │ └── Contents.json │ │ └── primaryForeground.colorset │ │ │ └── Contents.json │ ├── ContentView.swift │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── MVI │ │ ├── MVI+Mock.swift │ │ └── MVI.swift │ ├── Phoenix.entitlements │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── SceneDelegate.swift │ ├── colors.swift │ ├── cs.lproj │ │ ├── InfoPlist.strings │ │ └── Localizable.strings │ ├── es.lproj │ │ ├── InfoPlist.strings │ │ └── Localizable.strings │ ├── extensions │ │ ├── Data+Hexadecimal.swift │ │ ├── Int+TimeInterval.swift │ │ ├── Result+Deugly.swift │ │ ├── String+VersionComparison.swift │ │ ├── TextField+Verbatim.swift │ │ ├── UIApplicationState+Phoenix.swift │ │ └── UserDefaults+Codable.swift │ ├── fr.lproj │ │ ├── InfoPlist.strings │ │ └── Localizable.strings │ ├── kotlin │ │ ├── KotlinAssociatedObject.swift │ │ ├── KotlinBasics.swift │ │ ├── KotlinExtensions.swift │ │ ├── KotlinFlow.swift │ │ ├── KotlinFutures.swift │ │ └── KotlinPublishers.swift │ ├── security │ │ ├── AppSecurity.swift │ │ ├── EnabledSecurity.swift │ │ ├── GenericPasswordConvertible.swift │ │ ├── GenericPasswordStore.swift │ │ ├── KeyStoreError.swift │ │ └── SecurityFile.swift │ ├── sync │ │ ├── PendingSettings.swift │ │ ├── SyncManager.swift │ │ └── SyncManagerState.swift │ ├── uikitAppearance.swift │ ├── utils │ │ ├── AnimationCompletion.swift │ │ ├── Cache.swift │ │ ├── CurrencyPrefs.swift │ │ ├── CurrencyUnit.swift │ │ ├── DelayedSave.swift │ │ ├── FormattedAmount.swift │ │ ├── Prefs.swift │ │ ├── Utils.swift │ │ ├── ViewName.swift │ │ ├── lang.swift │ │ ├── publishers.swift │ │ ├── shapes.swift │ │ └── ui.swift │ └── views │ │ ├── ErrorView.swift │ │ ├── HomeView.swift │ │ ├── LockView.swift │ │ ├── PaymentView.swift │ │ ├── ReceiveView.swift │ │ ├── SendView.swift │ │ ├── configuration │ │ ├── ComingSoonView.swift │ │ ├── ConfigurationView.swift │ │ ├── advanced │ │ │ ├── ChannelsConfigurationView.swift │ │ │ ├── CloseChannelsView.swift │ │ │ └── ForceCloseChannelsView.swift │ │ ├── general │ │ │ ├── AboutView.swift │ │ │ ├── CloudOptionsView.swift │ │ │ ├── DisplayConfigurationView.swift │ │ │ ├── ElectrumConfigurationView.swift │ │ │ ├── PaymentOptionsView.swift │ │ │ └── TorConfigurationView.swift │ │ ├── logs │ │ │ ├── LogsConfigurationView.swift │ │ │ └── LogsConfigurationViewerView.swift │ │ └── security │ │ │ ├── AppAccessView.swift │ │ │ └── RecoverySeedView.swift │ │ ├── html │ │ ├── AboutHTML.swift │ │ ├── AdvancedSecurityHTML.swift │ │ ├── AnyHTML.swift │ │ ├── Base.lproj │ │ │ ├── about.html │ │ │ └── advancedSecurity.html │ │ ├── about.css │ │ ├── advancedSecurity.css │ │ ├── cs.lproj │ │ │ ├── about.html │ │ │ └── advancedSecurity.html │ │ ├── es.lproj │ │ │ ├── about.html │ │ │ └── advancedSecurity.html │ │ └── fr.lproj │ │ │ ├── about.html │ │ │ └── advancedSecurity.html │ │ ├── onboarding │ │ ├── InitializationView.swift │ │ ├── IntroContainer.swift │ │ ├── IntroView.swift │ │ └── RestoreWalletView.swift │ │ ├── style │ │ ├── EqualSizes.swift │ │ ├── ScaledButtonStyle.swift │ │ ├── SquareSize.swift │ │ ├── StyledString.swift │ │ └── TextFieldCurrencyStyler.swift │ │ └── widgets │ │ ├── AppStatusPopover.swift │ │ ├── CustomTextField.swift │ │ ├── HorizontalActivity.swift │ │ ├── InfoGrid.swift │ │ ├── LocalWebView.swift │ │ ├── Popover.swift │ │ ├── QRCode.swift │ │ ├── QRCodeScanner.swift │ │ ├── ShortSheet.swift │ │ └── Toast.swift ├── phoenix-iosTests │ ├── Info.plist │ └── phoenix-iosTests.swift └── phoenix-iosUITests │ ├── Info.plist │ └── phoenix-iosUITests.swift ├── phoenix-shared ├── build.gradle.kts └── src │ ├── androidMain │ ├── AndroidManifest.xml │ └── kotlin │ │ └── fr │ │ └── acinq │ │ └── phoenix │ │ ├── data │ │ └── androidElectrumRegtestConf.kt │ │ ├── db │ │ ├── SqlPaymentHooks.kt │ │ └── androidDbFactory.kt │ │ ├── managers │ │ └── NetworkMonitorAndroid.kt │ │ └── utils │ │ └── platformAndroid.kt │ ├── androidTest │ └── kotlin │ │ └── fr │ │ └── acinq │ │ └── phoenix │ │ └── db │ │ └── SqliteChannelsDatabaseTest.kt │ ├── commonMain │ ├── appdb │ │ └── fr.acinq.phoenix.db │ │ │ ├── BitcoinPriceRates.sq │ │ │ ├── KeyValueStore.sq │ │ │ ├── WalletParams.sq │ │ │ └── migrations │ │ │ └── 1.sqm │ ├── channelsdb │ │ └── fr.acinq.phoenix.db │ │ │ └── ChannelsDatabase.sq │ ├── kotlin │ │ └── fr.acinq.phoenix │ │ │ ├── PhoenixBusiness.kt │ │ │ ├── controllers │ │ │ ├── AppController.kt │ │ │ ├── ControllerFactory.kt │ │ │ ├── MVI.kt │ │ │ ├── config │ │ │ │ ├── ChannelsConfiguration.kt │ │ │ │ ├── ChannelsConfigurationController.kt │ │ │ │ ├── CloseChannelsConfiguration.kt │ │ │ │ ├── CloseChannelsConfigurationController.kt │ │ │ │ ├── Configuration.kt │ │ │ │ ├── ConfigurationController.kt │ │ │ │ ├── ElectrumConfiguration.kt │ │ │ │ ├── ElectrumConfigurationController.kt │ │ │ │ ├── LogsConfiguration.kt │ │ │ │ ├── LogsConfigurationController.kt │ │ │ │ └── LogsViewConfigurationController.kt │ │ │ ├── init │ │ │ │ ├── InitController.kt │ │ │ │ ├── Initialization.kt │ │ │ │ ├── RestoreWallet.kt │ │ │ │ └── RestoreWalletController.kt │ │ │ ├── main │ │ │ │ ├── Content.kt │ │ │ │ ├── ContentController.kt │ │ │ │ ├── Home.kt │ │ │ │ └── HomeController.kt │ │ │ └── payments │ │ │ │ ├── Receive.kt │ │ │ │ ├── ReceiveController.kt │ │ │ │ ├── Scan.kt │ │ │ │ └── ScanController.kt │ │ │ ├── data │ │ │ ├── AppConfiguration.kt │ │ │ ├── ExchangeRates.kt │ │ │ ├── LNUrl.kt │ │ │ ├── Wallet.kt │ │ │ ├── WalletContext.kt │ │ │ ├── electrumConfs.kt │ │ │ └── errors.kt │ │ │ ├── db │ │ │ ├── DbFactory.kt │ │ │ ├── SqliteAppDb.kt │ │ │ ├── SqliteChannelsDb.kt │ │ │ ├── SqlitePaymentsDb.kt │ │ │ ├── cloud │ │ │ │ ├── CloudHelper.kt │ │ │ │ ├── CloudSerializers.kt │ │ │ │ ├── CloudType.kt │ │ │ │ ├── IncomingType.kt │ │ │ │ ├── OutgoingPartType.kt │ │ │ │ └── OutgoingType.kt │ │ │ └── payments │ │ │ │ ├── CloudKitInterface.kt │ │ │ │ ├── DbTypesHelper.kt │ │ │ │ ├── IncomingOriginType.kt │ │ │ │ ├── IncomingQueries.kt │ │ │ │ ├── IncomingReceivedWithType.kt │ │ │ │ ├── OutgoingDetailsType.kt │ │ │ │ ├── OutgoingPartStatusType.kt │ │ │ │ ├── OutgoingQueries.kt │ │ │ │ └── OutgoingStatusType.kt │ │ │ ├── managers │ │ │ ├── AppConfigurationManager.kt │ │ │ ├── AppConnectionsDaemon.kt │ │ │ ├── ConnectionsManager.kt │ │ │ ├── CurrencyManager.kt │ │ │ ├── DatabaseManager.kt │ │ │ ├── LNUrlManager.kt │ │ │ ├── NetworkManager.kt │ │ │ ├── NodeParamsManager.kt │ │ │ ├── PaymentsFetcher.kt │ │ │ ├── PaymentsManager.kt │ │ │ ├── PeerManager.kt │ │ │ ├── Utilities.kt │ │ │ └── WalletManager.kt │ │ │ ├── mock.kt │ │ │ └── utils │ │ │ ├── Cache.kt │ │ │ ├── Connection.kt │ │ │ ├── LightningExtensions.kt │ │ │ ├── LogMemory.kt │ │ │ ├── PublicSuffixList.kt │ │ │ ├── SwiftFlow.kt │ │ │ ├── channelStates.kt │ │ │ ├── platform.kt │ │ │ └── stateFlow.kt │ └── paymentsdb │ │ └── fr.acinq.phoenix.db │ │ ├── AggregatedQueries.sq │ │ ├── CloudKitPayments.sq │ │ ├── IncomingPayments.sq │ │ ├── OutgoingPayments.sq │ │ └── migrations │ │ └── 1.sqm │ ├── commonTest │ └── kotlin │ │ └── fr │ │ └── acinq │ │ └── phoenix │ │ ├── TestConstants.kt │ │ ├── data │ │ ├── ApiWalletParamsTest.kt │ │ ├── WalletTest.kt │ │ └── lnurl │ │ │ ├── LNUrlBaseTest.kt │ │ │ └── LNUrlWithdrawTest.kt │ │ ├── db │ │ ├── IncomingPaymentDbTypeVersionTest.kt │ │ ├── OutgoingPaymentDbTypeVersionTest.kt │ │ ├── SqliteChannelsDatabaseTest.kt │ │ ├── SqlitePaymentsDatabaseTest.kt │ │ └── cloud │ │ │ └── CloudDataTest.kt │ │ ├── managers │ │ └── UtilitiesTest.kt │ │ └── utils │ │ ├── CacheTests.kt │ │ ├── ConnectionTests.kt │ │ └── PublicSuffixListTests.kt │ ├── iosMain │ └── kotlin │ │ └── fr │ │ └── acinq │ │ └── phoenix │ │ ├── data │ │ └── iosElectrumRegtestConf.kt │ │ ├── db │ │ ├── CloudKitDb.kt │ │ ├── CloudKitTypes.kt │ │ ├── SqlPaymentHooks.kt │ │ └── iosDbFactory.kt │ │ ├── managers │ │ └── NetwokMonitorIos.kt │ │ └── utils │ │ └── platformIos.kt │ └── iosTest │ └── kotlin │ └── fr │ └── acinq │ └── phoenix │ └── db │ └── SqliteChannelsDatabaseTest.kt └── settings.gradle.kts /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | build/ 3 | 4 | # Idea 5 | .idea 6 | *.iml 7 | 8 | # Gradle 9 | .gradle 10 | local.properties 11 | .gradletasknamecache 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # XCode 17 | xcuserdata/ 18 | 19 | *.iml 20 | target/ 21 | project/target 22 | DeleteMe*.scala 23 | 24 | # Android 25 | phoenix-android/debug/ 26 | phoenix-android/release/ -------------------------------------------------------------------------------- /.readme/phoenix_text.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2f5eeccba20f3dfeba2c7f8e5f929f863cfbfb53ec05c52a69d6ad76383fbdb5 3 | size 5486 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository is now in read-only mode for archiving purposes. It has been superseded by https://github.com/ACINQ/phoenix. 2 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath("com.android.tools.build:gradle:7.0.0") 9 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}") 10 | classpath("org.jetbrains.kotlin:kotlin-serialization:${Versions.kotlin}") 11 | classpath("com.squareup.sqldelight:gradle-plugin:${Versions.sqlDelight}") 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | google() 19 | mavenCentral() 20 | } 21 | } 22 | 23 | val clean by tasks.creating(Delete::class) { 24 | delete(rootProject.buildDir) 25 | } 26 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | repositories { 2 | jcenter() 3 | } 4 | plugins { 5 | `kotlin-dsl` 6 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | object Versions { 2 | const val lightningKmp = "1.0-beta17" 3 | const val secp256k1 = "0.5.1" 4 | 5 | const val kotlin = "1.4.32" 6 | const val coroutines = "1.4.3-native-mt" 7 | const val serialization = "1.1.0" 8 | const val datetime = "0.1.1" 9 | 10 | const val ktor = "1.5.3" 11 | const val sqlDelight = "1.5.0" 12 | const val kodeinMemory = "0.8.0" 13 | 14 | const val slf4j = "1.7.30" 15 | const val junit = "4.13" 16 | 17 | object Android { 18 | const val ktx = "1.5.0" 19 | const val lifecycle = "2.3.1" 20 | const val prefs = "1.1.1" 21 | const val compose = "1.0.0-beta07" 22 | const val navCompose = "2.4.0-alpha01" 23 | const val constraintLayoutCompose = "1.0.0-alpha07" 24 | const val zxing = "4.1.0" 25 | const val logback = "2.0.0" 26 | const val testRunner = "1.3.0" 27 | const val espresso = "3.3.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle 2 | org.gradle.jvmargs = -Xmx1536m 3 | org.gradle.parallel = true 4 | 5 | # Kotlin 6 | kotlin.code.style = official 7 | kotlin.mpp.stability.nowarn=true 8 | kotlin.incremental.multiplatform = true 9 | kotlin.parallel.tasks.in.project = true 10 | kotlin.mpp.enableGranularSourceSetsMetadata = true 11 | kotlin.native.enableDependencyPropagation = false 12 | 13 | # Android 14 | android.useAndroidX = true 15 | android.enableJetifier = true 16 | 17 | xcodeproj=phoenix-ios/phoenix-ios.xcodeproj 18 | 19 | # the chain that we use 20 | chain="testnet" -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /local.properties.template: -------------------------------------------------------------------------------- 1 | # This file defines whether Android is loaded by Gradle & IntelliJ. 2 | # At least one of both properties 'sdk.dir' or 'skip.android' must be set. 3 | 4 | # Must be set in order for Android to be loaded. 5 | #sdk.dir=/Location/to/Android/sdk 6 | 7 | # Instructs Gradle to skip Android. 8 | # Note that this property takes precedence over sdk.dir (you can have sdk.dir set and still skip Android). 9 | #skip.android=true 10 | -------------------------------------------------------------------------------- /phoenix-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /phoenix-android/src/main/java/fr/acinq/phoenix/android/PhoenixApplication.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.android 18 | 19 | import android.app.Application 20 | import fr.acinq.phoenix.PhoenixBusiness 21 | import fr.acinq.phoenix.android.utils.Logging 22 | import fr.acinq.phoenix.utils.PlatformContext 23 | 24 | class PhoenixApplication : Application() { 25 | 26 | val business by lazy { PhoenixBusiness(PlatformContext(this)) } 27 | 28 | override fun onCreate() { 29 | super.onCreate() 30 | Logging.setupLogger(applicationContext) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /phoenix-android/src/main/java/fr/acinq/phoenix/android/utils/QRCode.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.android.utils 18 | 19 | import android.graphics.Bitmap 20 | import android.graphics.Color 21 | import com.google.zxing.EncodeHintType 22 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel 23 | import com.google.zxing.qrcode.encoder.Encoder 24 | import java.util.* 25 | 26 | /** 27 | * Created by DPA on 19/06/19. 28 | */ 29 | object QRCode { 30 | 31 | /** Create a Bitmap QR code from a String. */ 32 | fun generateBitmap(source: String): Bitmap { 33 | val hintsMap = HashMap() 34 | hintsMap[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L 35 | hintsMap[EncodeHintType.MARGIN] = 0 36 | val qrCode = Encoder.encode(source, ErrorCorrectionLevel.L, hintsMap) 37 | val width = qrCode.matrix.width 38 | val height = qrCode.matrix.height 39 | val rgbArray = IntArray(width * height) 40 | var i = 0 41 | for (y in 0 until height) { 42 | for (x in 0 until width) { 43 | rgbArray[i] = if (qrCode.matrix.get(x, y) > 0) Color.BLACK else Color.WHITE 44 | i++ 45 | } 46 | } 47 | return Bitmap.createScaledBitmap(Bitmap.createBitmap(rgbArray, width, height, Bitmap.Config.RGB_565), 200, 200, false) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /phoenix-android/src/main/java/fr/acinq/phoenix/android/utils/extensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.android.utils 18 | 19 | /** 20 | * Utility method rebinding any exceptions thrown by a method into another exception, using the origin exception as the root cause. 21 | * Helps with pattern matching. 22 | */ 23 | inline fun tryWith(exception: Exception, action: () -> T): T = try { 24 | action.invoke() 25 | } catch (t: Exception) { 26 | exception.initCause(t) 27 | throw exception 28 | } 29 | -------------------------------------------------------------------------------- /phoenix-android/src/main/java/fr/acinq/phoenix/android/utils/logger.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.android.utils 18 | 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.remember 21 | import androidx.compose.ui.platform.LocalContext 22 | import androidx.compose.ui.platform.LocalView 23 | import fr.acinq.phoenix.android.PhoenixApplication 24 | import org.kodein.log.Logger 25 | import org.kodein.log.LoggerFactory 26 | import org.kodein.log.frontend.simplePrintFrontend 27 | import org.kodein.log.frontend.slf4jFrontend 28 | import org.kodein.log.newLogger 29 | 30 | 31 | @Composable 32 | fun logger(): Logger { 33 | val context = LocalContext.current 34 | val application = context.applicationContext 35 | 36 | if (application !is PhoenixApplication) { // Preview mode 37 | return remember { LoggerFactory(slf4jFrontend).newLogger(context::class) } 38 | } 39 | 40 | return remember { 41 | application.business.loggerFactory.newLogger(context::class) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_alert_triangle.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_arrow_next.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_blank.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_brush.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_chain.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_chevron_down.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_clipboard.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_copy.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_cross_circle.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_fire.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_help_circle.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_key.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 26 | 30 | 31 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 25 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_lock.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_notification.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_payment_failed.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_payment_pending.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 30 | 38 | 45 | 46 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_payment_success.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_payment_success_onchain.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_receive.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_restore.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_scan.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_shield.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_tool.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_unlock.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_user.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/ic_zap.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 22 | 29 | 30 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/drawable/line_dots.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/layout/scan_view.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 29 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a4e3eb573a5501ad81d9b542f0f0af90337e87349379306a20e6a2bb51088b53 3 | size 1838 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9c41c381dd93d5a8e97ab2b62a678841202bbe38a0371fe7069ed9b910804f87 3 | size 3720 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a884abb966864a9a73636f0d559257581ee613247b693d3b3405ff4c810e2e45 3 | size 1260 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1ba5ceb91dd47cf99c2b7d42c08d9848795cff3a26a2bf4369f5f596a4423b6a 3 | size 2378 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8c593662f7940854ffed3e5fd2e9492154d16316e13c48312778ca2cbc134429 3 | size 2516 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4d13ee3886680b13feb5fd7db32f07b50cec3eee5e0bd6a68ce14c2fe9e989cf 3 | size 5222 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fcdb244cf3b715b76fa841f3e40a2c98cf1ec4ecfd00c0b6323abfa02212c5db 3 | size 3863 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2cbff090f0a4a52ef062719847b7a9b9f00273b71c549678b616165107db4801 3 | size 8225 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5feee8a1b31678e1e16dd3f49f55dc22d75c4be1e0fc4c12f0e3826855e51c89 3 | size 5422 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7c82bcf42e4ffe560a5cff951a719981e23dee535872ee8e7e564a71e0c8110b 3 | size 11789 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #5741D9 20 | #50B338 21 | #1AC486 22 | #91B4D1 23 | #2b313e 24 | #ffffff 25 | #f0f5f7 26 | #3e4556 27 | 28 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | 32dp 5 | 6 | 18dp 7 | 8 | 9 | 250dp 10 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /phoenix-android/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_payment_sent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_payment_sent.svg", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "idiom" : "universal" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "compression-type" : "lossless", 23 | "preserves-vector-representation" : true, 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_payment_sent.imageset/ic_payment_sent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_payment_success_static.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_payment_success_static.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_payment_success_static.imageset/ic_payment_success_static.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_payment_success_static.imageset/ic_payment_success_static.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_receive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_receive.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_receive.imageset/ic_receive.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_receive.imageset/ic_receive.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_restore.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_restore.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_restore.imageset/ic_restore.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_restore.imageset/ic_restore.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_scan.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_scan.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_scan.imageset/ic_scan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_scan.imageset/ic_scan.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_send.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_send.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_send.imageset/ic_send.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_send.imageset/ic_send.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ic_settings.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true, 14 | "template-rendering-intent" : "template" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/ic_settings.imageset/ic_settings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/ic_settings.imageset/ic_settings.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_blue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_blue.imageset/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_blue_192.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo_192.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "logo_192@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_blue_192.imageset/logo_192.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:18488dbb788f9fc37c034f626336460fbd4913064548c0e670a0372328995ac0 3 | size 2810 4 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_blue_192.imageset/logo_192@2x.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:3b12fbb4557c653b8e6d29c3eaaf1c940cc75b1417e92a463eb00a31c8c792ac 3 | size 11825 4 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_green.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "phoenix.svg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_green.imageset/phoenix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_green_192.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo_green_192.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "logo_green_384.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_green_192.imageset/logo_green_192.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dee284f15c587fe3c9f6380a4b03fc4f5e7d1e9445407ae7db14c8146067dad3 3 | size 2044 4 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/logo_green_192.imageset/logo_green_384.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4e4a584887a9aa72e2517f992fa986003bccbaa2c71d1a9912270f8bb58e4ca8 3 | size 9782 4 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_failed.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "payment_holder_def_failed.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_failed.imageset/payment_holder_def_failed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_failed.imageset/payment_holder_def_failed.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_pending.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "payment_holder_def_pending.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_pending.imageset/payment_holder_def_pending.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_pending.imageset/payment_holder_def_pending.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_success.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "payment_holder_def_success.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_success.imageset/payment_holder_def_success.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACINQ/phoenix-kmm/3a335173860bb982d6681626e8e618094d95cb31/phoenix-ios/phoenix-ios/Assets.xcassets/payment_holder_def_success.imageset/payment_holder_def_success.pdf -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Assets.xcassets/testnet_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "testnet_light_path.svg", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "testnet_dark_path.svg", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | }, 22 | "properties" : { 23 | "preserves-vector-representation" : true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/appAccentBlue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "209", 9 | "green" : "180", 10 | "red" : "145" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "219", 27 | "green" : "190", 28 | "red" : "155" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/appAccentGreen.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x38", 9 | "green" : "0xB3", 10 | "red" : "0x50" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x62", 27 | "green" : "0xB9", 28 | "red" : "0x00" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/appNegative.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "79", 9 | "green" : "79", 10 | "red" : "209" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "89", 27 | "green" : "89", 28 | "red" : "219" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/appWarn.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x2E", 9 | "green" : "0xBC", 10 | "red" : "0xFE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "56", 27 | "green" : "198", 28 | "red" : "255" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/borderColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xED", 9 | "green" : "0xEB", 10 | "red" : "0xE1" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x6C", 27 | "green" : "0x58", 28 | "red" : "0x4E" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/buttonFill.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "0.990", 10 | "red" : "0.990" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "platform" : "ios", 24 | "reference" : "secondarySystemBackgroundColor" 25 | }, 26 | "idiom" : "universal" 27 | } 28 | ], 29 | "info" : { 30 | "author" : "xcode", 31 | "version" : 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/mutedBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x1D", 27 | "green" : "0x19", 28 | "red" : "0x16" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/primaryBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "250", 9 | "green" : "245", 10 | "red" : "245" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "platform" : "ios", 24 | "reference" : "systemBackgroundColor" 25 | }, 26 | "idiom" : "universal" 27 | }, 28 | { 29 | "appearances" : [ 30 | { 31 | "appearance" : "contrast", 32 | "value" : "high" 33 | } 34 | ], 35 | "color" : { 36 | "color-space" : "srgb", 37 | "components" : { 38 | "alpha" : "1.000", 39 | "blue" : "250", 40 | "green" : "245", 41 | "red" : "245" 42 | } 43 | }, 44 | "idiom" : "universal" 45 | }, 46 | { 47 | "appearances" : [ 48 | { 49 | "appearance" : "luminosity", 50 | "value" : "dark" 51 | }, 52 | { 53 | "appearance" : "contrast", 54 | "value" : "high" 55 | } 56 | ], 57 | "color" : { 58 | "platform" : "ios", 59 | "reference" : "systemBackgroundColor" 60 | }, 61 | "idiom" : "universal" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Colors.xcassets/primaryForeground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x3E", 9 | "green" : "0x31", 10 | "red" : "0x2B" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "platform" : "ios", 24 | "reference" : "labelColor" 25 | }, 26 | "idiom" : "universal" 27 | } 28 | ], 29 | "info" : { 30 | "author" : "xcode", 31 | "version" : 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 358118532563-kp8r74nv2p7ng3v3bqlrdha12tj8gf7v.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.358118532563-kp8r74nv2p7ng3v3bqlrdha12tj8gf7v 9 | ANDROID_CLIENT_ID 10 | 358118532563-6mk6vj5juts2ek7on7c8ej3fpd5686mr.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyCThStqY87eRgaLzC1kTe0zrPCtGMRleow 13 | GCM_SENDER_ID 14 | 358118532563 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | co.acinq.phoenix 19 | PROJECT_ID 20 | phoenix-testnet-dc948 21 | STORAGE_BUCKET 22 | phoenix-testnet-dc948.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:358118532563:ios:2312a2c09fd90a9ae4fb26 35 | DATABASE_URL 36 | https://phoenix-testnet-dc948.firebaseio.com 37 | 38 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Phoenix.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.icloud-container-identifiers 8 | 9 | iCloud.co.acinq.phoenix 10 | 11 | com.apple.developer.icloud-services 12 | 13 | CloudKit 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Salomon BRYS on 24/08/2020. 3 | // Copyright (c) 2020 Acinq. All rights reserved. 4 | // 5 | 6 | import SwiftUI 7 | 8 | extension Color { 9 | 10 | // See Colors.xcassets for RGB values. 11 | // The assets catalog allows us to customize the values for light vs dark modes. 12 | 13 | static var appAccent: Color { 14 | get { 15 | return Color(UIColor.appAccent) // see below: extension UIColor 16 | } 17 | } 18 | 19 | static var appPositive: Color = Color("appAccentGreen") 20 | static let appNegative = Color("appNegative") 21 | static let appWarn = Color("appWarn") 22 | 23 | static let buttonFill = Color("buttonFill") 24 | static let primaryBackground = Color("primaryBackground") 25 | static let primaryForeground = Color("primaryForeground") 26 | static let borderColor = Color("borderColor") 27 | static let mutedBackground = Color("mutedBackground") 28 | } 29 | 30 | extension UIColor { 31 | 32 | static var appAccent: UIColor { 33 | if AppDelegate.isTestnet { 34 | return UIColor(named: "appAccentBlue")! 35 | } else { 36 | return UIColor(named: "appAccentGreen")! 37 | } 38 | } 39 | 40 | static var primaryBackground: UIColor { 41 | return UIColor(named: "primaryBackground")! 42 | } 43 | 44 | /// Note that UITraitCollection.current may be incorrect if accessed from a background thread. 45 | /// 46 | func htmlString(_ traitCollection: UITraitCollection) -> String { 47 | 48 | let adaptedColor = self.resolvedColor(with: traitCollection) 49 | 50 | var r: CGFloat = 0 51 | var g: CGFloat = 0 52 | var b: CGFloat = 0 53 | var a: CGFloat = 0 54 | adaptedColor.getRed(&r, green: &g, blue: &b, alpha: &a) 55 | 56 | let ir = Int(r * 255.0) 57 | let ig = Int(g * 255.0) 58 | let ib = Int(b * 255.0) 59 | let sa = String(format: "%.2f", a) 60 | 61 | return "rgb(\(ir), \(ig), \(ib), \(sa))" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/cs.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Bundle name */ 2 | "CFBundleName" = "Phoenix"; 3 | 4 | /* Privacy - Camera Usage Description */ 5 | "NSCameraUsageDescription" = "Phoenix chce použít kameru k naskenování platební žádosti.\n"; 6 | 7 | /* Privacy - Face ID Usage Description */ 8 | "NSFaceIDUsageDescription" = "Přihlásit se pomocí Face ID."; 9 | 10 | /* Privacy - Photo Library Additions Usage Description */ 11 | "NSPhotoLibraryAddUsageDescription" = "Phoenix chce uložit obrázek QR kódu do knihovny fotografií."; 12 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/es.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Bundle name */ 2 | "CFBundleName" = "Phoenix"; 3 | 4 | /* Privacy - Camera Usage Description */ 5 | "NSCameraUsageDescription" = "A Phoenix le gustaría usar la cámara para escanear solicitudes de pago.\n"; 6 | 7 | /* Privacy - Face ID Usage Description */ 8 | "NSFaceIDUsageDescription" = "Iniciar sesión con Face ID."; 9 | 10 | /* Privacy - Photo Library Additions Usage Description */ 11 | "NSPhotoLibraryAddUsageDescription" = "A Phoenix le gustaría guardar una imagen de código QR en la biblioteca de fotos."; 12 | 13 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/Data+Hexadecimal.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Data { 4 | struct HexEncodingOptions: OptionSet { 5 | let rawValue: Int 6 | static let upperCase = HexEncodingOptions(rawValue: 1 << 0) 7 | } 8 | 9 | func hexEncodedString(options: HexEncodingOptions = []) -> String { 10 | let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx" 11 | return map { String(format: format, $0) }.joined() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/Int+TimeInterval.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | extension Int { 5 | 6 | func milliseconds() -> TimeInterval { 7 | return Double(self) / Double(1_000) 8 | } 9 | 10 | func seconds() -> TimeInterval { 11 | return Double(self) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/Result+Deugly.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // In earlier versions of Swift, you could setup an enum with Void type: 4 | // ``` 5 | // enum Result { 6 | // case success(T) 7 | // case error(Error?) 8 | // } 9 | // ``` 10 | // 11 | // And then you could omit the associated value of type Void: 12 | // ``` 13 | // finish(.success()) 14 | // ``` 15 | // 16 | // This no longer works as of Swift 4, and now you have to do one of: 17 | // ``` 18 | // finish(.success(())) 19 | // finish(.success(Void())) 20 | // ``` 21 | // 22 | // The community consensus is that this is "ugly". 23 | // And this extension restores the cleanliness of previous Swift versions. 24 | // 25 | extension Result where Success == Void { 26 | static var success: Result { 27 | return .success(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/String+VersionComparison.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Credit: 4 | /// - https://stackoverflow.com/a/59307884/43522 5 | /// - https://github.com/DragonCherry/VersionCompare 6 | 7 | extension String { 8 | 9 | private func compare(toVersion targetVersion: String) -> ComparisonResult { 10 | let versionDelimiter = "." 11 | var result: ComparisonResult = .orderedSame 12 | var versionComponents = components(separatedBy: versionDelimiter) 13 | var targetComponents = targetVersion.components(separatedBy: versionDelimiter) 14 | 15 | while versionComponents.count < targetComponents.count { 16 | versionComponents.append("0") 17 | } 18 | 19 | while targetComponents.count < versionComponents.count { 20 | targetComponents.append("0") 21 | } 22 | 23 | for (version, target) in zip(versionComponents, targetComponents) { 24 | result = version.compare(target, options: .numeric) 25 | if result != .orderedSame { 26 | break 27 | } 28 | } 29 | 30 | return result 31 | } 32 | 33 | func isVersion(equalTo targetVersion: String) -> Bool { 34 | return compare(toVersion: targetVersion) == .orderedSame 35 | } 36 | 37 | func isVersion(greaterThan targetVersion: String) -> Bool { 38 | return compare(toVersion: targetVersion) == .orderedDescending 39 | } 40 | 41 | func isVersion(greaterThanOrEqualTo targetVersion: String) -> Bool { 42 | return compare(toVersion: targetVersion) != .orderedAscending 43 | } 44 | 45 | func isVersion(lessThan targetVersion: String) -> Bool { 46 | return compare(toVersion: targetVersion) == .orderedAscending 47 | } 48 | 49 | func isVersion(lessThanOrEqualTo targetVersion: String) -> Bool { 50 | return compare(toVersion: targetVersion) != .orderedDescending 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/TextField+Verbatim.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | 4 | extension TextField where Label == Text { 5 | 6 | /// Text has a nice initializer we can use when we don't need to localize the string: 7 | /// ``` 8 | /// Text(verbatim: String) 9 | /// ``` 10 | /// 11 | /// TextField is missing this, so we're adding it here. 12 | init( 13 | verbatim: String, 14 | text: Binding, 15 | onEditingChanged: @escaping (Bool) -> Void = { _ in }, 16 | onCommit: @escaping () -> Void = {} 17 | ) { 18 | self.init(verbatim, text: text, onEditingChanged: onEditingChanged, onCommit: onCommit) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/UIApplicationState+Phoenix.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | 4 | extension UIApplication.State: CustomStringConvertible { 5 | 6 | public var description: String { 7 | switch self { 8 | case .inactive : return "inactive" 9 | case .active : return "active" 10 | case .background : return "background" 11 | default : return "unknown" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/extensions/UserDefaults+Codable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension UserDefaults { 4 | 5 | func getCodable(forKey key: String) -> Element? { 6 | guard let data = UserDefaults.standard.data(forKey: key) else { 7 | return nil 8 | } 9 | let element = try? JSONDecoder().decode(Element.self, from: data) 10 | return element 11 | } 12 | 13 | func setCodable(value: Element, forKey key: String) { 14 | let data = try? JSONEncoder().encode(value) 15 | UserDefaults.standard.setValue(data, forKey: key) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/fr.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Bundle name */ 2 | "CFBundleName" = "Phoenix"; 3 | 4 | /* Privacy - Camera Usage Description */ 5 | "NSCameraUsageDescription" = "Phoenix souhaiterait utiliser la caméra pour scanner des requêtes de paiement."; 6 | 7 | /* Privacy - Face ID Usage Description */ 8 | "NSFaceIDUsageDescription" = "Authentification avec Face ID"; 9 | 10 | /* Privacy - Photo Library Additions Usage Description */ 11 | "NSPhotoLibraryAddUsageDescription" = "Phoenix souhaiterait sauvegarder l’image du QR code dans votre bibliothèque de photos."; 12 | 13 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/kotlin/KotlinAssociatedObject.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PhoenixShared 3 | 4 | 5 | extension KotlinBase { 6 | 7 | static let queue = DispatchQueue.init(label: "KotlinBase-AssociatedObject") 8 | 9 | func executeOnce(storageKey key: UnsafeRawPointer, block: () -> T) -> T { 10 | KotlinBase.queue.sync { 11 | if let existingValue = objc_getAssociatedObject(self, key) as? T { 12 | return existingValue 13 | } else { 14 | let newValue = block() 15 | 16 | objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 17 | return newValue 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/kotlin/KotlinBasics.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PhoenixShared 3 | 4 | 5 | extension KotlinByteArray { 6 | 7 | func toSwiftData() -> Data { 8 | 9 | let size = self.size 10 | var data = Data(count: Int(size)) 11 | for idx in 0 ..< size { 12 | let byte: Int8 = self.get(index: idx) 13 | data[Int(idx)] = UInt8(bitPattern: byte) 14 | } 15 | return data 16 | } 17 | } 18 | 19 | extension Data { 20 | 21 | func toKotlinByteArray() -> KotlinByteArray { 22 | 23 | let result = KotlinByteArray(size: Int32(self.count)) 24 | for (idx, byte) in self.enumerated() { 25 | result.set(index: Int32(idx), value: Int8(bitPattern: byte)) 26 | } 27 | return result 28 | } 29 | } 30 | 31 | extension Array { 32 | 33 | func toKotlinArray() -> KotlinArray { 34 | 35 | return KotlinArray(size: Int32(self.count)) { (i: KotlinInt) in 36 | var shutUpCompiler: Element? = nil 37 | shutUpCompiler = (self[i.intValue] as! Element) 38 | return shutUpCompiler 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/kotlin/KotlinFutures.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Combine 3 | import PhoenixShared 4 | import os.log 5 | 6 | #if DEBUG && true 7 | fileprivate var log = Logger( 8 | subsystem: Bundle.main.bundleIdentifier!, 9 | category: "KotlinFutures" 10 | ) 11 | #else 12 | fileprivate var log = Logger(OSLog.disabled) 13 | #endif 14 | 15 | extension DatabaseManager { 16 | 17 | func getDatabases() -> Future { 18 | 19 | return Future { promise in 20 | 21 | let flow = SwiftStateFlow(origin: self.databases) 22 | 23 | var watcher: Ktor_ioCloseable? = nil 24 | watcher = flow.watch { (databases: Lightning_kmpDatabases?) in 25 | 26 | if let databases = databases { 27 | promise(.success(databases)) 28 | watcher?.close() 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/security/EnabledSecurity.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Represents the security options enabled by the user. 4 | /// 5 | struct EnabledSecurity: OptionSet, CustomStringConvertible { 6 | 7 | let rawValue: Int 8 | 9 | static let biometrics = EnabledSecurity(rawValue: 1 << 0) 10 | static let passphrase = EnabledSecurity(rawValue: 1 << 1) 11 | static let advancedSecurity = EnabledSecurity(rawValue: 1 << 2) 12 | 13 | static let none: EnabledSecurity = [] 14 | 15 | var description: String { 16 | var str = "[" 17 | if contains(.biometrics) { 18 | str.append("biometrics") 19 | } 20 | if contains(.passphrase) { 21 | if str.count > 1 { 22 | str.append(", ") 23 | } 24 | str.append("passphrase") 25 | } 26 | if contains(.advancedSecurity) { 27 | if str.count > 1 { 28 | str.append(", ") 29 | } 30 | str.append("advancedSecurity") 31 | } 32 | 33 | return str.appending("]") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/security/KeyStoreError.swift: -------------------------------------------------------------------------------- 1 | /** 2 | * Apple Sample Code: 3 | * https://developer.apple.com/documentation/cryptokit/storing_cryptokit_keys_in_the_keychain 4 | * 5 | * Abstract: 6 | * The interface required for conversion to a generic password keychain item. 7 | */ 8 | 9 | import Foundation 10 | 11 | /// An error we can throw when something goes wrong. 12 | struct KeyStoreError: Error, CustomStringConvertible { 13 | var message: String 14 | 15 | init(_ message: String) { 16 | self.message = message 17 | } 18 | 19 | public var description: String { 20 | return message 21 | } 22 | } 23 | 24 | extension OSStatus { 25 | 26 | /// A human readable message for the status. 27 | var message: String { 28 | return (SecCopyErrorMessageString(self, nil) as String?) ?? String(self) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/uikitAppearance.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Salomon BRYS on 20/11/2020. 3 | // Copyright (c) 2020 Acinq. All rights reserved. 4 | // 5 | 6 | import SwiftUI 7 | 8 | func UIKitAppearance() { 9 | let navBarAppearance = UINavigationBarAppearance() 10 | navBarAppearance.configureWithOpaqueBackground() 11 | navBarAppearance.backgroundColor = .primaryBackground 12 | UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance 13 | UINavigationBar.appearance().compactAppearance = navBarAppearance 14 | UINavigationBar.appearance().standardAppearance = navBarAppearance 15 | 16 | UITableView.appearance().backgroundColor = .clear 17 | UITableView.appearance().separatorStyle = .none 18 | UITableViewCell.appearance().backgroundColor = .clear 19 | } 20 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/CurrencyUnit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import PhoenixShared 3 | 4 | /// Represents a displayable currency, 5 | /// which can be either a BitcoinUnit or a FiatCurrency. 6 | /// 7 | enum Currency: Hashable { 8 | case bitcoin(BitcoinUnit) 9 | case fiat(FiatCurrency) 10 | 11 | var abbrev: String { 12 | switch self { 13 | case .bitcoin(let unit): 14 | return unit.shortName 15 | case .fiat(let currency): 16 | return currency.shortName 17 | } 18 | } 19 | } 20 | 21 | extension Currency { 22 | 23 | /// A list of all BitcoinUnit's and the currently selected FiatCurrency (IFF we know the exchangeRate). 24 | /// 25 | static func displayable(currencyPrefs: CurrencyPrefs) -> [Currency] { 26 | 27 | var all = [Currency]() 28 | 29 | for bitcoinUnit in BitcoinUnit.default().values { 30 | all.append(Currency.bitcoin(bitcoinUnit)) 31 | } 32 | 33 | let fiatCurrency = currencyPrefs.fiatCurrency 34 | if let _ = currencyPrefs.fiatExchangeRate(fiatCurrency: fiatCurrency) { 35 | all.append(Currency.fiat(fiatCurrency)) 36 | } else { 37 | // We don't have the exchange rate for the user's selected fiat currency. 38 | // So we won't be able to perform conversion to millisatoshi. 39 | } 40 | 41 | return all 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/DelayedSave.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Combine 3 | 4 | class DelayedSave { 5 | 6 | private var cancellables = Set() 7 | private var timer: Timer? = nil 8 | private var needsSave: Bool = false 9 | private var saveAction: (() -> Void)? = nil 10 | 11 | init() { 12 | 13 | let nc = NotificationCenter.default 14 | nc.publisher(for: UIApplication.willResignActiveNotification).sink {[weak self] _ in 15 | self?.saveIfNeeded() 16 | }.store(in: &cancellables) 17 | } 18 | 19 | deinit { 20 | timer?.invalidate() 21 | } 22 | 23 | func save(withDelay delay: TimeInterval, action: @escaping () -> Void) { 24 | 25 | assert(Thread.isMainThread, "This function is restricted to the main-thread") 26 | 27 | needsSave = true 28 | saveAction = action 29 | 30 | timer?.invalidate() 31 | timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: {[weak self] _ in 32 | self?.saveIfNeeded() 33 | }) 34 | } 35 | 36 | func saveIfNeeded() { 37 | if needsSave { 38 | needsSave = false 39 | 40 | timer?.invalidate() 41 | timer = nil 42 | 43 | saveAction?() 44 | saveAction = nil 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/ViewName.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Add this protocol to your view: 4 | /// ``` 5 | /// struct MyView: View, ViewName 6 | /// ``` 7 | /// 8 | /// And then you can quickly include the name of the view in log statements: 9 | /// ``` 10 | /// log.trace("[\(viewName)] onAppear()") 11 | /// ``` 12 | /// 13 | protocol ViewName { 14 | var viewName: String { get } 15 | } 16 | 17 | extension ViewName { 18 | 19 | var viewName: String { 20 | get { 21 | let thisType = type(of: self) 22 | return String(describing: thisType) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/lang.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Salomon BRYS on 20/08/2020. 3 | // Copyright (c) 2020 Acinq. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | extension Int64 { 9 | 10 | /// Do NOT use this for currency values. 11 | /// For currency, use Utils.format(...) 12 | /// 13 | func formatInDecimalStyle() -> String { 14 | let formatter = NumberFormatter() 15 | formatter.numberStyle = .decimal 16 | formatter.usesGroupingSeparator = true 17 | return formatter.string(from: NSNumber(value: self))! 18 | } 19 | 20 | func formatDateMS() -> String { 21 | let formatter = DateFormatter() 22 | formatter.dateStyle = .long 23 | formatter.timeStyle = .short 24 | let date = Date(timeIntervalSince1970: TimeInterval(self / 1000)) 25 | return formatter.string(from: date) 26 | } 27 | 28 | func formatDateS() -> String { 29 | let formatter = DateFormatter() 30 | formatter.dateStyle = .long 31 | formatter.timeStyle = .short 32 | let date = Date(timeIntervalSince1970: TimeInterval(self)) 33 | return formatter.string(from: date) 34 | } 35 | } 36 | 37 | extension Int32 { 38 | 39 | /// Do NOT use this for currency values. 40 | /// For currency, use Utils.format(...) 41 | /// 42 | func formatInDecimalStyle() -> String { 43 | let formatter = NumberFormatter() 44 | formatter.numberStyle = .decimal 45 | formatter.usesGroupingSeparator = true 46 | return formatter.string(from: NSNumber(value: self))! 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/publishers.swift: -------------------------------------------------------------------------------- 1 | import Combine 2 | import UIKit 3 | 4 | struct KeyboardInfo { 5 | var height: CGFloat = 0 6 | var animationCurve: UIView.AnimationCurve = UIView.AnimationCurve.easeInOut 7 | var animationDuration: TimeInterval = 0.0 8 | } 9 | 10 | extension Publishers { 11 | static var keyboardInfo: AnyPublisher { 12 | let willShow = NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification) 13 | .map { $0.keyboardInfo } 14 | 15 | let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification) 16 | .map { _ in KeyboardInfo() } 17 | 18 | return MergeMany(willShow, willHide) 19 | .eraseToAnyPublisher() 20 | } 21 | } 22 | 23 | extension Notification { 24 | 25 | var keyboardInfo: KeyboardInfo { 26 | var info = KeyboardInfo() 27 | if let value = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect { 28 | info.height = value.height 29 | } 30 | if let value = userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber { 31 | if let curve = UIView.AnimationCurve(rawValue: value.intValue) { 32 | info.animationCurve = curve 33 | } 34 | } 35 | if let value = userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber { 36 | info.animationDuration = value.doubleValue 37 | } 38 | return info 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/utils/shapes.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct Line: Shape { 4 | func path(in rect: CGRect) -> Path { 5 | var path = Path() 6 | path.move(to: CGPoint(x: 0, y: 0)) 7 | path.addLine(to: CGPoint(x: rect.width, y: 0)) 8 | return path 9 | } 10 | } -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/configuration/ComingSoonView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ComingSoonView: View { 4 | 5 | let title: String 6 | 7 | var body: some View { 8 | 9 | VStack(alignment: HorizontalAlignment.center) { 10 | 11 | Text("Coming Soon") 12 | .padding(.top, 40) 13 | 14 | Spacer() 15 | } 16 | .navigationBarTitle(title) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/configuration/general/AboutView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import PhoenixShared 3 | import WebKit 4 | 5 | struct AboutView: View { 6 | 7 | @Environment(\.colorScheme) var colorScheme 8 | 9 | @ViewBuilder 10 | var body: some View { 11 | 12 | VStack(alignment: HorizontalAlignment.center, spacing: 0) { 13 | 14 | LocalWebView( 15 | html: AboutHTML(), 16 | scrollIndicatorInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -20) 17 | ) 18 | .frame(maxWidth: .infinity, maxHeight: .infinity) 19 | .padding(.leading, 20) 20 | .padding(.trailing, 20) // must match LocalWebView.scrollIndicatorInsets.right 21 | 22 | Text("Version \(versionString())") 23 | .padding([.top, .bottom], 4) 24 | .frame(maxWidth: .infinity, minHeight: 40) 25 | .background( 26 | Color( 27 | colorScheme == ColorScheme.light 28 | ? UIColor.systemGroupedBackground 29 | : UIColor.secondarySystemGroupedBackground 30 | ) 31 | .edgesIgnoringSafeArea(.bottom) // background color should extend to bottom of screen 32 | ) 33 | } 34 | .navigationBarTitle( 35 | NSLocalizedString("About", comment: "Navigation bar title"), 36 | displayMode: .inline 37 | ) 38 | } 39 | 40 | func versionString() -> String { 41 | return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?" 42 | } 43 | } 44 | 45 | class AboutView_Previews : PreviewProvider { 46 | 47 | static var previews: some View { 48 | AboutView() 49 | .previewDevice("iPhone 11") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/configuration/general/TorConfigurationView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct TorConfigurationView: View { 4 | 5 | @State var isTorEnabled = Prefs.shared.isTorEnabled 6 | @State var theme = Prefs.shared.theme 7 | 8 | var body: some View { 9 | Form { 10 | Section(header: TorFormHeader(), content: {}).textCase(nil) 11 | 12 | Toggle(isOn: $isTorEnabled.animation()) { 13 | if isTorEnabled { 14 | Text("Tor is enabled") 15 | } else { 16 | Text("Tor is disabled") 17 | } 18 | }.onChange(of: isTorEnabled) { newValue in 19 | self.toggleTor(newValue) 20 | } 21 | } 22 | .frame(maxWidth: .infinity, maxHeight: .infinity) 23 | .edgesIgnoringSafeArea(.bottom) 24 | .navigationBarTitle( 25 | NSLocalizedString("Tor Settings", comment: "Navigation bar title"), 26 | displayMode: .inline 27 | ) 28 | } 29 | 30 | struct TorFormHeader: View { 31 | 32 | var body: some View { 33 | Text( 34 | """ 35 | You can improve your privacy by only using Tor when connecting to an Electrum server or \ 36 | to your Lightning peer. This will slightly slow down your transactions. 37 | """ 38 | ) 39 | .font(.body) 40 | .foregroundColor(Color.primary) 41 | .padding(.top, 10) 42 | } 43 | } 44 | 45 | func toggleTor(_ isEnabled: Bool) { 46 | Prefs.shared.isTorEnabled = isEnabled 47 | } 48 | } 49 | 50 | class TorConfigurationView_Previews: PreviewProvider { 51 | 52 | static var previews: some View { 53 | TorConfigurationView() 54 | .previewDevice("iPhone 11") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/configuration/logs/LogsConfigurationViewerView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import PhoenixShared 3 | 4 | struct LogsConfigurationViewerView: View { 5 | 6 | let filePath: String 7 | 8 | @State var share: NSURL? = nil 9 | 10 | @State var text: String? = nil 11 | 12 | var body: some View { 13 | VStack { 14 | if let text = text { 15 | ScrollView { 16 | Text(text.prefix(250000)) // SwiftUI Text seems to have issues displaying very large texts 17 | .font(.system(.callout, design: .monospaced)) 18 | .frame(maxWidth: .infinity, alignment: .leading) 19 | } 20 | } else { 21 | EmptyView() 22 | } 23 | } 24 | .navigationBarItems( 25 | trailing: Button { 26 | share = NSURL(fileURLWithPath: filePath) 27 | } label: { 28 | Image(systemName: "square.and.arrow.up") 29 | } 30 | .sharing($share) 31 | ) 32 | .onAppear { 33 | DispatchQueue.global(qos: .userInitiated).async { 34 | do { 35 | let logs = try String(contentsOfFile: filePath) 36 | DispatchQueue.main.async { text = logs } 37 | } catch { 38 | DispatchQueue.main.async { text = "Could not load \(filePath)" } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | class LogsConfigurationViewerView_Previews: PreviewProvider { 46 | 47 | static var previews: some View { 48 | 49 | NavigationView { 50 | LogsConfigurationViewerView(filePath: "fake") 51 | } 52 | .previewDevice("iPhone 11") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/AboutHTML.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | class AboutHTML: AnyHTML { 4 | 5 | init() { 6 | super.init(filename: "about") 7 | } 8 | 9 | // Nothing else to override here. 10 | // The defaults work fine for us. 11 | } 12 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/AdvancedSecurityHTML.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | class AdvancedSecurityHTML: AnyHTML { 4 | 5 | init() { 6 | super.init(filename: "advancedSecurity") 7 | } 8 | 9 | // Nothing else to override here. 10 | // The defaults work fine for us. 11 | } 12 | 13 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/Base.lproj/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Phoenix is a Bitcoin wallet using the Lightning network 11 | for sending and receiving payments. 12 |

13 |

14 | It is a free open source software, developed by 15 | ACINQ under the 16 | Apache 2.0 License. 17 |

18 |

19 | Note that this wallet is non-custodial: 20 | you have sole custody of the wallet's 12-word seed. 21 | Do not give this seed to anyone, and beware of phishing. 22 |

23 |

24 | If you have questions, consult the FAQ. 25 | You can also contact support for help. 26 |

27 |

28 | Bitcoin/fiat exchange rates are retrieved from various third-party APIs: 29 |

30 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/Base.lproj/advancedSecurity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Standard security:

11 |
    12 |
  • Access to the user interface is protected by biometrics.
  • 13 |
  • Phoenix can receive incoming payments as long as the app is running.
  • 14 |
15 | 16 |

Advanced security:

17 |
    18 |
  • Access to your seed is protected by biometrics.
  • 19 |
  • Phoenix can receive incoming payments as long as the app is running AND you've unlocked the app at least once.
  • 20 |
21 | 22 |

Reduced app functionality:

23 |

24 | With advanced security enabled: If the device is restarted, and Phoenix is re-launched automatically (e.g. operating system upgrade), then incoming payments will fail until you unlock Phoenix. 25 |

26 | 27 |

Technical details:

28 |

29 | Your seed is wrapped using ChaCha20-Poly1305 with a 256-bit lockingKey, and the resulting {ciphertext, nonce, tag} is stored in a JSON file on disk. The lockingKey is stored in the iOS keychain. 30 |

31 |

32 | With normal security, the app can access the lockingKey on app launch, and immediately unlock the bitcoin wallet. Enabled security options restrict access to the user interface. 33 |

34 |

35 | With advanced security, the lockingKey is encumbered such that it cannot be accessed unless you authenticate with biometrics. Thus the app is incapable of unlocking the bitcoin wallet without your assistance. 36 |

37 | 38 | 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/about.css: -------------------------------------------------------------------------------- 1 | /* Credits: 2 | * https://useyourloaf.com/blog/using-dynamic-type-with-web-views/ 3 | * https://useyourloaf.com/blog/supporting-dark-mode-in-wkwebview 4 | **/ 5 | 6 | :root { 7 | background: [[background_color]]; 8 | color: [[foreground_color]]; 9 | --link-color: [[link_color]]; 10 | } 11 | 12 | body { 13 | font: -apple-system-body; 14 | } 15 | 16 | a { 17 | color: var(--link-color); 18 | } 19 | 20 | p { 21 | margin-top: 0pt; 22 | margin-bottom: 0pt; 23 | padding-top: 5pt; 24 | padding-bottom: 10pt; 25 | } 26 | 27 | p.noBottomPadding { 28 | padding-bottom: 0pt; 29 | } 30 | 31 | ul { 32 | margin-top: 0; 33 | padding-top: 0; 34 | } 35 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/advancedSecurity.css: -------------------------------------------------------------------------------- 1 | /* Credits: 2 | * https://useyourloaf.com/blog/using-dynamic-type-with-web-views/ 3 | * https://useyourloaf.com/blog/supporting-dark-mode-in-wkwebview 4 | **/ 5 | 6 | :root { 7 | background: [[background_color]]; 8 | color: [[foreground_color]]; 9 | --link-color: [[link_color]]; 10 | } 11 | 12 | body { 13 | font: -apple-system-body; 14 | } 15 | 16 | a { 17 | color: var(--link-color); 18 | } 19 | 20 | p { 21 | margin-top: 0pt; 22 | margin-bottom: 0pt; 23 | padding-top: 5pt; 24 | padding-bottom: 5pt; 25 | } 26 | 27 | ul { 28 | margin-top: 0; 29 | padding-top: 0; 30 | } 31 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/cs.lproj/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Phoenix is a Bitcoin wallet using the Lightning network 11 | for sending and receiving payments. 12 |

13 |

14 | It is a free open source software, developed by 15 | ACINQ under the 16 | Apache 2.0 License. 17 |

18 |

19 | Note that this wallet is non-custodial: 20 | you have sole custody of the wallet's 12-word seed. 21 | Do not give this seed to anyone, and beware of phishing. 22 |

23 |

24 | If you have questions, consult the FAQ. 25 | You can also contact support for help. 26 |

27 |

28 | Bitcoin/fiat exchange rates are retrieved from various third-party APIs: 29 |

30 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/cs.lproj/advancedSecurity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Standard security:

11 |
    12 |
  • Access to the user interface is protected by biometrics.
  • 13 |
  • Phoenix can receive incoming payments as long as the app is running.
  • 14 |
15 | 16 |

Advanced security:

17 |
    18 |
  • Access to your seed is protected by biometrics.
  • 19 |
  • Phoenix can receive incoming payments as long as the app is running AND you've unlocked the app at least once.
  • 20 |
21 | 22 |

Reduced app functionality:

23 |

24 | With advanced security enabled: If the device is restarted, and Phoenix is re-launched automatically (e.g. operating system upgrade), then incoming payments will fail until you unlock Phoenix. 25 |

26 | 27 |

Technical details:

28 |

29 | Your seed is wrapped using ChaCha20-Poly1305 with a 256-bit lockingKey, and the resulting {ciphertext, nonce, tag} is stored in a JSON file on disk. The lockingKey is stored in the iOS keychain. 30 |

31 |

32 | With normal security, the app can access the lockingKey on app launch, and immediately unlock the bitcoin wallet. Enabled security options restrict access to the user interface. 33 |

34 |

35 | With advanced security, the lockingKey is encumbered such that it cannot be accessed unless you authenticate with biometrics. Thus the app is incapable of unlocking the bitcoin wallet without your assistance. 36 |

37 | 38 | 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/es.lproj/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Phoenix es una billetera Bitcoin que usa la red de Lightning para enviar y recibir pagos. 11 |

12 |

13 | Es un software gratuito de código abierto, desarrollado por 14 | ACINQ bajo la licencia 15 | Apache 2.0 License. 16 |

17 |

18 | Tenga en cuenta que esta billetera no tiene custodia: 19 | usted tiene la custodia exclusiva de la semilla de 12 palabras de la billetera. 20 | No le dé esta semilla a nadie y tenga cuidado con el fraude. 21 |

22 |

23 | Si tiene preguntas, consulte las preguntas frecuentes. 24 | También puede ponerse en contacto con soporte técnico para obtener ayuda. 25 |

26 |

27 | Los tipos de cambio de Bitcoin / Fiat se obtienen de varias API de terceros: 28 |

29 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/es.lproj/advancedSecurity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

Seguridad estándar:

11 |
    12 |
  • El acceso a la interfaz de usuario está protegido por biometría.
  • 13 |
  • Phoenix puede recibir pagos siempre que la aplicación se esté ejecutando.
  • 14 |
15 | 16 |

Seguridad avanzada:

17 |
    18 |
  • El acceso a su semilla está protegido por biometría.
  • 19 |
  • Phoenix puede recibir pagos siempre que la aplicación se esté ejecutando Y haya desbloqueado la aplicación al menos una vez.
  • 20 |
21 | 22 |

Funcionalidad reducida de la aplicación:

23 |

24 | Con la seguridad avanzada habilitada: si el dispositivo se reinicia y Phoenix se reinicia automáticamente (por ejemplo, actualización del sistema operativo), los pagos entrantes fallarán hasta que desbloquee Phoenix. 25 |

26 | 27 |

Technical details:

28 |

29 | Your seed is wrapped using ChaCha20-Poly1305 with a 256-bit lockingKey, and the resulting {ciphertext, nonce, tag} is stored in a JSON file on disk. The lockingKey is stored in the iOS keychain. 30 |

31 |

32 | With normal security, the app can access the lockingKey on app launch, and immediately unlock the bitcoin wallet. Enabled security options restrict access to the user interface. 33 |

34 |

35 | With advanced security, the lockingKey is encumbered such that it cannot be accessed unless you authenticate with biometrics. Thus the app is incapable of unlocking the bitcoin wallet without your assistance. 36 |

37 | 38 | 39 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/html/fr.lproj/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 | Phoenix est un portefeuille Bitcoin qui utilise le réseau Lightning pour envoyer et recevoir des paiements. 11 |

12 |

13 | Ce logiciel libre et open source est développé par ACINQ sous licence Apache 2.0. 14 |

15 |

16 | Notez que ce portefeuille est non-custodial, c'est-à-dire que vous seul avez possession de la clé (ou "seed") du portefeuille.. Ne révélez cette clé à personne, et faites attention au hameçonnage. 17 |

18 |

19 | Si vous avez des questions, consulter la FAQ. Vous pouvez également contacter le support si vous avez besoin d'aide. 20 |

21 |

22 | Le taux de conversion Bitcoin/devise fiduciaire sont récupérés depuis des services tiers et sont présents à titre indicatifs : 23 |

24 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/onboarding/IntroContainer.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import os.log 3 | 4 | #if DEBUG && true 5 | fileprivate var log = Logger( 6 | subsystem: Bundle.main.bundleIdentifier!, 7 | category: "IntroContainer" 8 | ) 9 | #else 10 | fileprivate var log = Logger(OSLog.disabled) 11 | #endif 12 | 13 | 14 | struct IntroContainer: View { 15 | 16 | @State var introFinished = false 17 | 18 | @ViewBuilder 19 | var body: some View { 20 | 21 | ZStack { 22 | 23 | if introFinished { 24 | 25 | InitializationView() 26 | .zIndex(0) 27 | 28 | } else { 29 | 30 | IntroView(finish: introScreensFinished) 31 | .zIndex(1) // needed for proper animation 32 | .transition(.asymmetric( 33 | insertion : .identity, 34 | removal : .move(edge: .bottom) 35 | )) 36 | } 37 | } 38 | } 39 | 40 | func introScreensFinished() -> Void { 41 | log.trace("introScreenFinished()") 42 | 43 | withAnimation { 44 | introFinished = true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/style/EqualSizes.swift: -------------------------------------------------------------------------------- 1 | /// Credit to this genius with the **wonderful** write-up: 2 | /// https://finestructure.co/blog/2020/1/20/swiftui-equal-widths-view-constraints 3 | /// 4 | 5 | import SwiftUI 6 | 7 | struct GeometryPreferenceReader where K.Value == V { 8 | let key: K.Type 9 | let value: (GeometryProxy) -> V 10 | } 11 | 12 | extension GeometryPreferenceReader: ViewModifier { 13 | func body(content: Content) -> some View { 14 | content 15 | .background(GeometryReader { 16 | Color.clear.preference(key: self.key, value: self.value($0)) 17 | }) 18 | } 19 | } 20 | 21 | protocol Preference {} 22 | 23 | struct AppendValue: PreferenceKey { 24 | typealias Value = [CGFloat] 25 | static var defaultValue: Value { [] } 26 | static func reduce(value: inout Value, nextValue: () -> Value) { 27 | value.append(contentsOf: nextValue()) 28 | } 29 | } 30 | 31 | extension View { 32 | func assignMaxPreference( 33 | for key: K.Type, 34 | to binding: Binding 35 | ) -> some View where K.Value == [CGFloat] { 36 | 37 | return self.onPreferenceChange(key.self) { prefs in 38 | let maxPref = prefs.reduce(0, max) 39 | if maxPref > 0 { 40 | // only set value if > 0 to avoid pinning sizes to zero 41 | binding.wrappedValue = maxPref 42 | } 43 | } 44 | } 45 | 46 | func read(_ preference: GeometryPreferenceReader) -> some View { 47 | modifier(preference) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/style/SquareSize.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | // Expands the frame such that the wider of the {width, height} 4 | // becomes the size of the square. 5 | // 6 | // The view's preferred size is first measured before applying the frame modifier. 7 | // 8 | // Exmple: 9 | // ``` 10 | // Image(systemName: "bolt.fill") 11 | // .imageScale(.large) 12 | // .font(.caption2) 13 | // .squareFrame() 14 | // ``` 15 | // 16 | struct SquareFrameWrapper: View { 17 | 18 | let alignment: Alignment 19 | let content: () -> Content 20 | 21 | enum SquareSize: Preference {} 22 | let squareSizeReader = GeometryPreferenceReader( 23 | key: AppendValue.self, 24 | value: { [$0.size.width, $0.size.height] } 25 | ) 26 | @State var squareSize: CGFloat? = nil 27 | 28 | @ViewBuilder 29 | var body: some View { 30 | 31 | content() 32 | .read(squareSizeReader) 33 | .frame(width: squareSize, height: squareSize, alignment: alignment) 34 | .assignMaxPreference(for: squareSizeReader.key, to: $squareSize) 35 | } 36 | } 37 | 38 | struct SquareFrameModifier: ViewModifier { 39 | let alignment: Alignment 40 | 41 | func body(content: Content) -> some View { 42 | SquareFrameWrapper(alignment: alignment) { 43 | content 44 | } 45 | } 46 | } 47 | 48 | extension View { 49 | func squareFrame(alignment: Alignment = .center) -> some View { 50 | ModifiedContent( 51 | content: self, 52 | modifier: SquareFrameModifier(alignment: alignment) 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/widgets/CustomTextField.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CustomTextField: UIViewRepresentable { 4 | 5 | class Coordinator: NSObject, UITextFieldDelegate { 6 | 7 | @Binding var text: String 8 | var didBecomeFirstResponder = false 9 | 10 | init(text: Binding) { 11 | _text = text 12 | } 13 | 14 | func textFieldDidChangeSelection(_ textField: UITextField) { 15 | text = textField.text ?? "" 16 | } 17 | 18 | } 19 | 20 | @Binding var text: String 21 | var isFirstResponder: Bool = false 22 | var keyboardType: UIKeyboardType = .default 23 | var returnKeyType: UIReturnKeyType = .default 24 | var placeholder: String? = nil 25 | 26 | func makeUIView(context: UIViewRepresentableContext) -> UITextField { 27 | let textField = UITextField(frame: .zero) 28 | textField.keyboardType = keyboardType 29 | textField.returnKeyType = returnKeyType 30 | textField.delegate = context.coordinator 31 | textField.placeholder = placeholder 32 | return textField 33 | } 34 | 35 | func makeCoordinator() -> CustomTextField.Coordinator { 36 | Coordinator(text: $text) 37 | } 38 | 39 | func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext) { 40 | uiView.text = text 41 | if isFirstResponder && !context.coordinator.didBecomeFirstResponder { 42 | uiView.becomeFirstResponder() 43 | context.coordinator.didBecomeFirstResponder = true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-ios/views/widgets/QRCode.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftUI 3 | 4 | class QRCode : ObservableObject { 5 | @Published var value: String? = nil 6 | @Published var cgImage: CGImage? = nil 7 | @Published var image: Image? = nil 8 | 9 | func generate(value: String) { 10 | if value == self.value { return } 11 | self.value = value 12 | self.cgImage = nil 13 | self.image = nil 14 | 15 | DispatchQueue.global(qos: .userInitiated).async { 16 | let data = value.data(using: .ascii) 17 | guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { 18 | fatalError("No CIQRCodeGenerator") 19 | } 20 | qrFilter.setValue(data, forKey: "inputMessage") 21 | let cgTransform = CGAffineTransform(scaleX: 8, y: 8) 22 | guard let ciImage = qrFilter.outputImage?.transformed(by: cgTransform) else { 23 | fatalError("Could not scale QRCode") 24 | } 25 | guard let cgImage = CIContext().createCGImage(ciImage, from: ciImage.extent) else { 26 | fatalError("Could not generate QRCode image") 27 | } 28 | let image = Image(decorative: cgImage, scale: 1.0) 29 | DispatchQueue.main.async { 30 | if value == self.value { 31 | self.cgImage = cgImage 32 | self.image = image 33 | } else { 34 | // Ignore: qrCode value has changed since we started image generation 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-iosTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-iosTests/phoenix-iosTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import appName 3 | 4 | class appNameTests: XCTestCase { 5 | 6 | override func setUp() { 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | } 13 | 14 | func testExample() { 15 | // This is an example of a functional test case. 16 | // Use XCTAssert and related functions to verify your tests produce the correct results. 17 | } 18 | 19 | func testPerformanceExample() { 20 | // This is an example of a performance test case. 21 | self.measure { 22 | // Put the code you want to measure the time of here. 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-iosUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /phoenix-ios/phoenix-iosUITests/phoenix-iosUITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class appNameUITests: XCTestCase { 4 | 5 | override func setUp() { 6 | // Put setup code here. This method is called before the invocation of each test method in the class. 7 | 8 | // In UI tests it is usually best to stop immediately when a failure occurs. 9 | continueAfterFailure = false 10 | 11 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 12 | } 13 | 14 | override func tearDown() { 15 | // Put teardown code here. This method is called after the invocation of each test method in the class. 16 | } 17 | 18 | func testExample() { 19 | // UI tests must launch the application that they test. 20 | let app = XCUIApplication() 21 | app.launch() 22 | 23 | // Use recording to get started writing UI tests. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testLaunchPerformance() { 28 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 29 | // This measures how long it takes to launch your application. 30 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 31 | XCUIApplication().launch() 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /phoenix-shared/src/androidMain/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /phoenix-shared/src/androidMain/kotlin/fr/acinq/phoenix/data/androidElectrumRegtestConf.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.data 2 | 3 | actual fun platformElectrumRegtestConf(): ElectrumAddress = ElectrumAddress(host = "10.0.2.2", tcpPort = 51001, sslPort = 51002) 4 | -------------------------------------------------------------------------------- /phoenix-shared/src/androidMain/kotlin/fr/acinq/phoenix/db/SqlPaymentHooks.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.db 2 | 3 | import fr.acinq.phoenix.db.payments.CloudKitInterface 4 | 5 | actual fun didCompletePaymentRow(id: PaymentRowId, database: PaymentsDatabase): Unit {} 6 | 7 | actual fun makeCloudKitDb(database: PaymentsDatabase): CloudKitInterface? { 8 | return null 9 | } -------------------------------------------------------------------------------- /phoenix-shared/src/androidMain/kotlin/fr/acinq/phoenix/db/androidDbFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.db 18 | 19 | import com.squareup.sqldelight.android.AndroidSqliteDriver 20 | import com.squareup.sqldelight.db.SqlDriver 21 | import fr.acinq.phoenix.data.Chain 22 | import fr.acinq.phoenix.utils.PlatformContext 23 | import java.util.* 24 | 25 | actual fun createChannelsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver { 26 | return AndroidSqliteDriver(ChannelsDatabase.Schema, ctx.applicationContext, "channels-${chain.name.toLowerCase(Locale.ROOT)}-$nodeIdHash.sqlite") 27 | } 28 | 29 | actual fun createPaymentsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver { 30 | return AndroidSqliteDriver(PaymentsDatabase.Schema, ctx.applicationContext, "payments-${chain.name.toLowerCase(Locale.ROOT)}-$nodeIdHash.sqlite") 31 | } 32 | 33 | actual fun createAppDbDriver(ctx: PlatformContext): SqlDriver { 34 | return AndroidSqliteDriver(AppDatabase.Schema, ctx.applicationContext, "appdb.sqlite") 35 | } -------------------------------------------------------------------------------- /phoenix-shared/src/androidMain/kotlin/fr/acinq/phoenix/utils/platformAndroid.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | import android.content.Context 4 | 5 | 6 | actual class PlatformContext(val applicationContext: Context) 7 | 8 | actual fun getApplicationFilesDirectoryPath(ctx: PlatformContext): String = 9 | ctx.applicationContext.filesDir.absolutePath 10 | 11 | actual fun getTemporaryDirectoryPath(ctx: PlatformContext): String = 12 | ctx.applicationContext.cacheDir.absolutePath 13 | -------------------------------------------------------------------------------- /phoenix-shared/src/androidTest/kotlin/fr/acinq/phoenix/db/SqliteChannelsDatabaseTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.db 18 | 19 | import com.squareup.sqldelight.db.SqlDriver 20 | import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver 21 | 22 | actual fun testChannelsDriver(): SqlDriver { 23 | val driver: SqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) 24 | ChannelsDatabase.Schema.create(driver) 25 | return driver 26 | } 27 | 28 | actual fun testPaymentsDriver(): SqlDriver { 29 | val driver: SqlDriver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) 30 | PaymentsDatabase.Schema.create(driver) 31 | return driver 32 | } 33 | 34 | actual fun isIOS(): Boolean { 35 | return false 36 | } -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/appdb/fr.acinq.phoenix.db/BitcoinPriceRates.sq: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS bitcoin_price_rates ( 2 | fiat_code TEXT NOT NULL PRIMARY KEY, 3 | price_per_btc REAL NOT NULL, 4 | source TEXT NOT NULL, 5 | updated_at INTEGER NOT NULL 6 | ); 7 | 8 | insert: 9 | INSERT INTO bitcoin_price_rates(fiat_code, price_per_btc, source, updated_at) VALUES (?, ?, ?, ?); 10 | 11 | update: 12 | UPDATE bitcoin_price_rates SET price_per_btc=?, source=?, updated_at=? WHERE fiat_code=?; 13 | 14 | get: 15 | SELECT * FROM bitcoin_price_rates WHERE fiat_code=?; 16 | 17 | list: 18 | SELECT * FROM bitcoin_price_rates; 19 | 20 | delete: 21 | DELETE FROM bitcoin_price_rates WHERE fiat_code=?; 22 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/appdb/fr.acinq.phoenix.db/KeyValueStore.sq: -------------------------------------------------------------------------------- 1 | -- Generic key/value store 2 | 3 | CREATE TABLE IF NOT EXISTS key_value_store ( 4 | key TEXT NOT NULL PRIMARY KEY, 5 | value BLOB NOT NULL, 6 | updated_at INTEGER NOT NULL 7 | ); 8 | 9 | get: 10 | SELECT * FROM key_value_store WHERE key = ?; 11 | 12 | exists: 13 | SELECT COUNT(*) FROM key_value_store WHERE key = ?; 14 | 15 | insert: 16 | INSERT INTO key_value_store(key, value, updated_at) VALUES (?, ?, ?); 17 | 18 | update: 19 | UPDATE key_value_store SET value = ?, updated_at = ? WHERE key = ?; 20 | 21 | delete: 22 | DELETE FROM key_value_store WHERE key = ?; -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/appdb/fr.acinq.phoenix.db/WalletParams.sq: -------------------------------------------------------------------------------- 1 | -- wallet parameters 2 | CREATE TABLE IF NOT EXISTS wallet_params ( 3 | version TEXT NOT NULL PRIMARY KEY, 4 | data TEXT NOT NULL, 5 | updated_at INTEGER NOT NULL 6 | ); 7 | 8 | -- queries 9 | 10 | insert: 11 | INSERT INTO wallet_params(version, data, updated_at) VALUES (?, ?, ?); 12 | 13 | update: 14 | UPDATE wallet_params SET data=?, updated_at=? WHERE version=?; 15 | 16 | get: 17 | SELECT * FROM wallet_params WHERE version=?; 18 | 19 | delete: 20 | DELETE FROM wallet_params WHERE version=?; -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/appdb/fr.acinq.phoenix.db/migrations/1.sqm: -------------------------------------------------------------------------------- 1 | -- Migration: v1 -> v2 2 | -- 3 | -- Changes: 4 | -- * Added table key_value_store 5 | 6 | CREATE TABLE IF NOT EXISTS key_value_store ( 7 | key TEXT NOT NULL PRIMARY KEY, 8 | value BLOB NOT NULL, 9 | updated_at INTEGER NOT NULL 10 | ); 11 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/channelsdb/fr.acinq.phoenix.db/ChannelsDatabase.sq: -------------------------------------------------------------------------------- 1 | PRAGMA foreign_keys = 1; 2 | 3 | -- channels table 4 | -- note: boolean are stored as INTEGER, with 0=false 5 | CREATE TABLE IF NOT EXISTS local_channels ( 6 | channel_id BLOB NOT NULL PRIMARY KEY, 7 | data BLOB NOT NULL, 8 | is_closed INTEGER AS Boolean DEFAULT 0 NOT NULL 9 | ); 10 | 11 | -- htlcs info table 12 | CREATE TABLE IF NOT EXISTS htlc_infos ( 13 | channel_id BLOB NOT NULL, 14 | commitment_number INTEGER NOT NULL, 15 | payment_hash BLOB NOT NULL, 16 | cltv_expiry INTEGER NOT NULL, 17 | FOREIGN KEY(channel_id) REFERENCES local_channels(channel_id) 18 | ); 19 | 20 | CREATE INDEX IF NOT EXISTS htlc_infos_idx ON htlc_infos(channel_id, commitment_number); 21 | 22 | -- channels queries 23 | getChannel: 24 | SELECT * FROM local_channels WHERE channel_id=?; 25 | 26 | updateChannel: 27 | UPDATE local_channels SET data=? WHERE channel_id=?; 28 | 29 | insertChannel: 30 | INSERT INTO local_channels VALUES (?, ?, 0); 31 | 32 | closeLocalChannel: 33 | UPDATE local_channels SET is_closed=1 WHERE channel_id=?; 34 | 35 | listLocalChannels: 36 | SELECT data FROM local_channels WHERE is_closed=0; 37 | 38 | -- htlcs info queries 39 | insertHtlcInfo: 40 | INSERT INTO htlc_infos VALUES (?, ?, ?, ?); 41 | 42 | listHtlcInfos: 43 | SELECT payment_hash, cltv_expiry FROM htlc_infos WHERE channel_id=? AND commitment_number=?; 44 | 45 | deleteHtlcInfo: 46 | DELETE FROM htlc_infos WHERE channel_id=?; 47 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/MVI.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers 2 | 3 | object MVI { 4 | 5 | abstract class Data { 6 | override fun toString(): String = this::class.simpleName ?: super.toString() 7 | } 8 | 9 | abstract class Model : Data() 10 | 11 | abstract class Intent : Data() 12 | 13 | abstract class Controller(val firstModel: M) { 14 | 15 | abstract fun subscribe(onModel: (M) -> Unit): () -> Unit 16 | 17 | abstract fun intent(intent: I) 18 | 19 | abstract fun stop() 20 | 21 | open class Mock(val model: M) : Controller(model) { 22 | override fun subscribe(onModel: (M) -> Unit): () -> Unit { 23 | onModel(model) 24 | return ({}) 25 | } 26 | override fun intent(intent: I) {} 27 | override fun stop() {} 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/ChannelsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.bitcoin.Satoshi 4 | import fr.acinq.phoenix.controllers.MVI 5 | 6 | object ChannelsConfiguration { 7 | 8 | data class Model( 9 | val nodeId: String, 10 | val json: String, 11 | val channels: List 12 | ) : MVI.Model() { 13 | 14 | data class Channel( 15 | val id: String, 16 | val isOk: Boolean, 17 | val stateName: String, 18 | val localBalance: Satoshi?, 19 | val remoteBalance: Satoshi?, 20 | val json: String, 21 | val txUrl: String? 22 | ) 23 | } 24 | 25 | val emptyModel = Model("{}", "", emptyList()) 26 | 27 | sealed class Intent: MVI.Intent() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/CloseChannelsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.bitcoin.ByteVector32 4 | import fr.acinq.phoenix.controllers.MVI 5 | 6 | object CloseChannelsConfiguration { 7 | 8 | sealed class Model : MVI.Model() { 9 | 10 | object Loading : Model() 11 | data class Ready(val channels: List, val address: String) : Model() 12 | data class ChannelsClosed(val channels: List) : Model() 13 | 14 | data class ChannelInfo( 15 | val id: ByteVector32, 16 | val balance: Long, // in sats 17 | val status: ChannelInfoStatus 18 | ) 19 | 20 | enum class ChannelInfoStatus { 21 | Normal, 22 | Offline, 23 | Syncing, 24 | Closing, 25 | Closed, 26 | Aborted 27 | } 28 | } 29 | 30 | sealed class Intent : MVI.Intent() { 31 | data class MutualCloseAllChannels(val address: String) : Intent() 32 | object ForceCloseAllChannels : Intent() 33 | } 34 | } -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/Configuration.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.phoenix.controllers.MVI 4 | 5 | object Configuration { 6 | 7 | sealed class Model : MVI.Model() { 8 | object SimpleMode : Model() 9 | object FullMode : Model() 10 | } 11 | 12 | sealed class Intent : MVI.Intent() 13 | } 14 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/ConfigurationController.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.phoenix.PhoenixBusiness 4 | import fr.acinq.phoenix.managers.WalletManager 5 | import fr.acinq.phoenix.controllers.AppController 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.launch 8 | import org.kodein.log.LoggerFactory 9 | 10 | @OptIn(ExperimentalCoroutinesApi::class) 11 | class AppConfigurationController( 12 | loggerFactory: LoggerFactory, 13 | private val walletManager: WalletManager 14 | ) : AppController( 15 | loggerFactory = loggerFactory, 16 | firstModel = Configuration.Model.SimpleMode 17 | ) { 18 | constructor(business: PhoenixBusiness): this( 19 | loggerFactory = business.loggerFactory, 20 | walletManager = business.walletManager 21 | ) 22 | 23 | init { 24 | launch { 25 | model( 26 | if (walletManager.wallet.value == null) 27 | Configuration.Model.SimpleMode 28 | else 29 | Configuration.Model.FullMode 30 | ) 31 | } 32 | } 33 | 34 | override fun process(intent: Configuration.Intent) = error("Nothing to process") 35 | } 36 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/ElectrumConfiguration.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.lightning.utils.Connection 4 | import fr.acinq.lightning.utils.ServerAddress 5 | import fr.acinq.phoenix.controllers.MVI 6 | import fr.acinq.phoenix.data.ElectrumConfig 7 | 8 | object ElectrumConfiguration { 9 | 10 | data class Model( 11 | val configuration: ElectrumConfig? = null, 12 | val currentServer: ServerAddress? = null, 13 | val connection: Connection = Connection.CLOSED, 14 | val feeRate: Long = 0, 15 | val blockHeight: Int = 0, 16 | val tipTimestamp: Long = 0, 17 | val walletIsInitialized: Boolean = false, 18 | val error: Error? = null 19 | ) : MVI.Model() { 20 | fun isCustom() = configuration != null && configuration is ElectrumConfig.Custom 21 | } 22 | 23 | sealed class Intent : MVI.Intent() { 24 | data class UpdateElectrumServer(val address: String?) : Intent() 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/LogsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.config 2 | 3 | import fr.acinq.phoenix.controllers.MVI 4 | 5 | object LogsConfiguration { 6 | 7 | sealed class Model : MVI.Model() { 8 | object Loading : Model() 9 | data class Ready(val path: String) : Model() 10 | } 11 | 12 | sealed class Intent : MVI.Intent() 13 | 14 | } 15 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/config/LogsViewConfigurationController.kt: -------------------------------------------------------------------------------- 1 | //package fr.acinq.phoenix.app.ctrl.config 2 | // 3 | //import fr.acinq.phoenix.app.ctrl.AppController 4 | //import fr.acinq.phoenix.ctrl.config.LogsViewConfiguration 5 | //import fr.acinq.phoenix.utils.LogMemory 6 | //import kotlinx.coroutines.Dispatchers 7 | //import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | //import kotlinx.coroutines.launch 9 | //import kotlinx.coroutines.withContext 10 | //import org.kodein.log.LoggerFactory 11 | //import org.kodein.memory.file.name 12 | //import org.kodein.memory.file.openReadableFile 13 | //import org.kodein.memory.file.resolve 14 | //import org.kodein.memory.text.readString 15 | //import org.kodein.memory.use 16 | // 17 | //@OptIn(ExperimentalCoroutinesApi::class) 18 | //class AppLogsViewConfigurationController(val fileName: String, loggerFactory: LoggerFactory, logMemory: LogMemory) 19 | // : AppController( 20 | // loggerFactory, 21 | // LogsViewConfiguration.Model.Loading(logMemory.directory.resolve(fileName).path) 22 | // ) { 23 | // 24 | // init { 25 | // launch { 26 | // if (fileName == logMemory.file.name) { 27 | // logMemory.rotate().join() 28 | // } 29 | // val filePath = logMemory.directory.resolve(fileName) 30 | // val str = withContext(Dispatchers.Default) { 31 | // filePath.openReadableFile().use { it.readString() } 32 | // } 33 | // model(LogsViewConfiguration.Model.Content(filePath.path, str)) 34 | // } 35 | // } 36 | // 37 | // override fun process(intent: LogsViewConfiguration.Intent) = error("Nothing to process") 38 | //} 39 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/init/InitController.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.init 2 | 3 | import fr.acinq.bitcoin.MnemonicCode 4 | import fr.acinq.phoenix.PhoenixBusiness 5 | import fr.acinq.phoenix.controllers.AppController 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.launch 8 | import org.kodein.log.LoggerFactory 9 | 10 | 11 | @OptIn(ExperimentalCoroutinesApi::class) 12 | class AppInitController( 13 | loggerFactory: LoggerFactory 14 | ) : AppController( 15 | loggerFactory = loggerFactory, 16 | firstModel = Initialization.Model.Ready 17 | ) { 18 | constructor(business: PhoenixBusiness): this( 19 | loggerFactory = business.loggerFactory 20 | ) 21 | 22 | override fun process(intent: Initialization.Intent) { 23 | when (intent) { 24 | is Initialization.Intent.GenerateWallet -> { 25 | launch { 26 | val mnemonics = MnemonicCode.toMnemonics(intent.entropy) 27 | val seed = MnemonicCode.toSeed(mnemonics, "") 28 | model(Initialization.Model.GeneratedWallet(mnemonics, seed)) 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/init/Initialization.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.init 2 | 3 | import fr.acinq.phoenix.controllers.MVI 4 | 5 | object Initialization { 6 | 7 | sealed class Model : MVI.Model() { 8 | object Ready : Model() 9 | data class GeneratedWallet(val mnemonics: List, val seed: ByteArray) : Model() { 10 | override fun toString() = "GeneratedWallet" 11 | } 12 | } 13 | 14 | sealed class Intent : MVI.Intent() { 15 | data class GenerateWallet(val entropy: ByteArray) : Intent() { 16 | override fun toString() = "GenerateWallet" 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/main/Content.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.main 2 | 3 | import fr.acinq.phoenix.controllers.MVI 4 | 5 | object Content { 6 | 7 | sealed class Model : MVI.Model() { 8 | object Waiting : Model() 9 | object IsInitialized : Model() 10 | object NeedInitialization : Model() 11 | } 12 | 13 | sealed class Intent : MVI.Intent() 14 | 15 | } 16 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/main/ContentController.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.main 2 | 3 | import fr.acinq.phoenix.PhoenixBusiness 4 | import fr.acinq.phoenix.controllers.AppController 5 | import fr.acinq.phoenix.managers.WalletManager 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.flow.first 8 | import kotlinx.coroutines.launch 9 | import org.kodein.log.LoggerFactory 10 | 11 | @OptIn(ExperimentalCoroutinesApi::class) 12 | class AppContentController( 13 | loggerFactory: LoggerFactory, 14 | private val walletManager: WalletManager 15 | ) : AppController( 16 | loggerFactory = loggerFactory, 17 | firstModel = Content.Model.Waiting 18 | ) { 19 | constructor(business: PhoenixBusiness): this( 20 | loggerFactory = business.loggerFactory, 21 | walletManager = business.walletManager 22 | ) 23 | 24 | init { 25 | launch { 26 | if (walletManager.wallet.value != null) { 27 | model(Content.Model.IsInitialized) 28 | } else { 29 | model(Content.Model.NeedInitialization) 30 | // Suspends until a wallet is created 31 | walletManager.wallet.first { it != null } 32 | model(Content.Model.IsInitialized) 33 | } 34 | } 35 | } 36 | 37 | override fun process(intent: Content.Intent) = error("Nothing to process") 38 | } 39 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/main/Home.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.main 2 | 3 | import fr.acinq.lightning.MilliSatoshi 4 | import fr.acinq.phoenix.controllers.MVI 5 | 6 | object Home { 7 | 8 | data class Model( 9 | val balance: MilliSatoshi, 10 | val incomingBalance: MilliSatoshi?, 11 | val paymentsCount: Long 12 | ) : MVI.Model() 13 | 14 | val emptyModel = Model( 15 | balance = MilliSatoshi(0), 16 | incomingBalance = null, 17 | paymentsCount = 0 18 | ) 19 | 20 | sealed class Intent : MVI.Intent() 21 | } 22 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/controllers/payments/Receive.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.controllers.payments 2 | 3 | import fr.acinq.lightning.MilliSatoshi 4 | import fr.acinq.phoenix.controllers.MVI 5 | 6 | object Receive { 7 | 8 | sealed class Model : MVI.Model() { 9 | object Awaiting : Model() 10 | object Generating: Model() 11 | data class Generated(val request: String, val paymentHash: String, val amount: MilliSatoshi?, val desc: String?): Model() 12 | sealed class SwapIn: Model() { 13 | object Requesting: SwapIn() 14 | data class Generated(val address: String): SwapIn() 15 | } 16 | 17 | } 18 | 19 | sealed class Intent : MVI.Intent() { 20 | data class Ask(val amount: MilliSatoshi?, val desc: String?) : Intent() 21 | object RequestSwapIn : Intent() 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/data/ExchangeRates.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.data 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | data class BitcoinPriceRate( 6 | val fiatCurrency: FiatCurrency, 7 | val price: Double, 8 | val source: String, 9 | val timestampMillis: Long, 10 | ) 11 | 12 | @Serializable data class BlockchainInfoPriceObject(val last: Double) 13 | @Serializable data class MxnApiResponse(val success: Boolean, val payload: MxnPriceRate) 14 | @Serializable data class MxnPriceRate(val last: Double) 15 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/data/electrumConfs.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.data 2 | 3 | 4 | data class ElectrumAddress( 5 | val host: String, 6 | val sslPort: Int = 50002, 7 | val tcpPort: Int = 50001, 8 | val version: String = "1.4" 9 | ) 10 | 11 | val electrumMainnetConfigurations = listOf( 12 | ElectrumAddress(host = "electrum.acinq.co"), 13 | ElectrumAddress(host = "E-X.not.fyi"), 14 | ElectrumAddress(host = "VPS.hsmiths.com"), 15 | ElectrumAddress(host = "btc.cihar.com"), 16 | ElectrumAddress(host = "e.keff.org"), 17 | ElectrumAddress(host = "electrum.qtornado.com"), 18 | ElectrumAddress(host = "electrum.emzy.de"), 19 | ElectrumAddress(host = "tardis.bauerj.eu"), 20 | ElectrumAddress(host = "ecdsa.net", sslPort = 110), 21 | ElectrumAddress(host = "e2.keff.org"), 22 | ElectrumAddress(host = "electrum3.hodlister.co"), 23 | ElectrumAddress(host = "electrum5.hodlister.co"), 24 | ElectrumAddress(host = "fortress.qtornado.com"), 25 | ElectrumAddress(host = "electrumx.erbium.eu"), 26 | ElectrumAddress(host = "electrum.bitkoins.nl", sslPort = 50512), 27 | ElectrumAddress(host = "electrum.blockstream.info"), 28 | ElectrumAddress(host = "blockstream.info", 700) 29 | ) 30 | 31 | val electrumTestnetConfigurations = listOf( 32 | ElectrumAddress(host = "testnet.qtornado.com", sslPort= 51002), 33 | ElectrumAddress(host = "tn.not.fyi", sslPort= 55002), 34 | ElectrumAddress(host = "testnet1.electrum.acinq.co", sslPort = 51002), 35 | ElectrumAddress(host = "blockstream.info", sslPort = 993), 36 | ElectrumAddress(host = "testnet.aranguren.org", sslPort = 51002), 37 | ) 38 | 39 | expect fun platformElectrumRegtestConf(): ElectrumAddress 40 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/data/errors.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.data 2 | 3 | object InvalidElectrumAddress: Error("Invalid electrum address.") 4 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/DbFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.db 18 | 19 | import com.squareup.sqldelight.db.SqlDriver 20 | import fr.acinq.bitcoin.PublicKey 21 | import fr.acinq.phoenix.data.Chain 22 | import fr.acinq.phoenix.utils.PlatformContext 23 | 24 | expect fun createChannelsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver 25 | 26 | expect fun createPaymentsDbDriver(ctx: PlatformContext, chain: Chain, nodeIdHash: String): SqlDriver 27 | 28 | expect fun createAppDbDriver(ctx: PlatformContext): SqlDriver -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/cloud/CloudHelper.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.db.cloud 2 | 3 | import io.ktor.util.* 4 | 5 | // Kotlin wants to encode a ByteArray like this: { 6 | // "fail": [123,34,112,97,121,109,101,110,116,82,101,113,117] 7 | // } 8 | // 9 | // Lol. If we don't use Cbor, then we should at least use Base64. 10 | 11 | @OptIn(InternalAPI::class) 12 | fun ByteArray.b64Encode(): String { 13 | return this.encodeBase64() // io.ktor.util 14 | } 15 | 16 | @OptIn(InternalAPI::class) 17 | fun String.b64Decode(): ByteArray { 18 | return this.decodeBase64Bytes() // io.ktor.util 19 | } 20 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/CloudKitInterface.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.db.payments 2 | 3 | /* Cross-platform placeholder for CloudKitDb. */ 4 | interface CloudKitInterface { 5 | } -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/db/payments/DbTypesHelper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.db.payments 18 | 19 | import io.ktor.utils.io.charsets.* 20 | import io.ktor.utils.io.core.* 21 | import kotlinx.serialization.json.Json 22 | import kotlinx.serialization.modules.SerializersModule 23 | import kotlinx.serialization.modules.polymorphic 24 | import kotlinx.serialization.modules.subclass 25 | 26 | object DbTypesHelper { 27 | /** Decode a byte array and apply a deserialization handler. */ 28 | fun decodeBlob(blob: ByteArray, handler: (String, Json) -> T) = handler(String(bytes = blob, charset = Charsets.UTF_8), Json) 29 | 30 | val module = SerializersModule { 31 | polymorphic(IncomingReceivedWithData.Part::class) { 32 | subclass(IncomingReceivedWithData.Part.Htlc.V0::class) 33 | subclass(IncomingReceivedWithData.Part.NewChannel.V0::class) 34 | } 35 | } 36 | 37 | val polymorphicFormat = Json { serializersModule = module } 38 | } -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/managers/NetworkManager.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.managers 2 | 3 | import fr.acinq.phoenix.utils.PlatformContext 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.StateFlow 6 | import org.kodein.log.LoggerFactory 7 | 8 | enum class NetworkState { 9 | Available, 10 | NotAvailable 11 | } 12 | 13 | @OptIn(ExperimentalCoroutinesApi::class) 14 | expect class NetworkManager(loggerFactory: LoggerFactory, ctx: PlatformContext) { 15 | val networkState: StateFlow 16 | fun start() 17 | fun stop() 18 | } 19 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/managers/WalletManager.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.managers 2 | 3 | import fr.acinq.bitcoin.ByteVector 4 | import fr.acinq.phoenix.data.Chain 5 | import fr.acinq.phoenix.data.Wallet 6 | import kotlinx.coroutines.CoroutineScope 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.MainScope 9 | import kotlinx.coroutines.flow.MutableStateFlow 10 | import kotlinx.coroutines.flow.StateFlow 11 | 12 | @OptIn(ExperimentalCoroutinesApi::class) 13 | class WalletManager( 14 | private val chain: Chain 15 | ) : CoroutineScope by MainScope() { 16 | 17 | private val _wallet = MutableStateFlow(null) 18 | val wallet: StateFlow = _wallet 19 | 20 | fun loadWallet(seed: ByteArray) { 21 | val newWallet = Wallet(seed, chain) 22 | _wallet.value = newWallet 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/Connection.kt: -------------------------------------------------------------------------------- 1 | import fr.acinq.lightning.utils.Connection 2 | 3 | operator fun Connection.plus(other: Connection) : Connection = 4 | when { 5 | this == other -> this 6 | this == Connection.ESTABLISHING || other == Connection.ESTABLISHING -> Connection.ESTABLISHING 7 | this == Connection.CLOSED || other == Connection.CLOSED -> Connection.CLOSED 8 | else -> error("Cannot add [$this + $other]") 9 | } -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/SwiftFlow.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | import io.ktor.utils.io.core.* 4 | import kotlinx.coroutines.* 5 | import kotlinx.coroutines.channels.* 6 | import kotlinx.coroutines.flow.* 7 | 8 | /* Credit: 9 | * https://github.com/JetBrains/kotlinconf-app/ 10 | * 11 | * (See file in project called FlowUtils.kt) 12 | */ 13 | 14 | // This doesn't work in Swift. 15 | // For some reason, in Swift, it is exposed as a function within SwiftFlow itself. 16 | /* 17 | @ExperimentalCoroutinesApi 18 | fun Flow.wrap() = SwiftFlow(this) 19 | */ 20 | 21 | @ExperimentalCoroutinesApi 22 | class SwiftFlow(private val origin: Flow) : Flow by origin { 23 | fun watch(block: (T) -> Unit): Closeable { 24 | val job = Job() 25 | 26 | onEach { 27 | block(it) 28 | }.launchIn(CoroutineScope(MainScope().coroutineContext + job)) 29 | 30 | return object : Closeable { 31 | override fun close() { 32 | job.cancel() 33 | } 34 | } 35 | } 36 | } 37 | 38 | @ExperimentalCoroutinesApi 39 | class SwiftStateFlow(private val origin: StateFlow) : StateFlow by origin { 40 | fun watch(block: (T) -> Unit): Closeable { 41 | val job = Job() 42 | 43 | onEach { 44 | block(it) 45 | }.launchIn(CoroutineScope(MainScope().coroutineContext + job)) 46 | 47 | return object : Closeable { 48 | override fun close() { 49 | job.cancel() 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/channelStates.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | import fr.acinq.lightning.channel.ChannelState 4 | import fr.acinq.lightning.channel.ChannelStateWithCommitments 5 | import fr.acinq.lightning.channel.Offline 6 | import fr.acinq.lightning.channel.Syncing 7 | import fr.acinq.lightning.transactions.CommitmentSpec 8 | 9 | 10 | val ChannelState.localCommitmentSpec: CommitmentSpec? get() = 11 | when (this) { 12 | is ChannelStateWithCommitments -> commitments.localCommit.spec 13 | is Offline -> state.localCommitmentSpec 14 | is Syncing -> state.localCommitmentSpec 15 | else -> null 16 | } 17 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/platform.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | 4 | expect class PlatformContext 5 | 6 | expect fun getApplicationFilesDirectoryPath(ctx: PlatformContext): String 7 | expect fun getTemporaryDirectoryPath(ctx: PlatformContext): String 8 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/stateFlow.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlin.reflect.KProperty 6 | 7 | @ExperimentalCoroutinesApi 8 | operator fun MutableStateFlow.setValue(thisRef: Any?, property: KProperty<*>, value: T) { 9 | this.value = value 10 | } 11 | 12 | @ExperimentalCoroutinesApi 13 | operator fun MutableStateFlow.getValue(thisRef: Any?, property: KProperty<*>): T { 14 | return this.value 15 | } 16 | 17 | -------------------------------------------------------------------------------- /phoenix-shared/src/commonMain/paymentsdb/fr.acinq.phoenix.db/migrations/1.sqm: -------------------------------------------------------------------------------- 1 | -- Migration: v1 -> v2 2 | -- 3 | -- Changes: 4 | -- * Added table cloudkit_payments_metadata 5 | -- * Added table cloudkit_payments_queue 6 | 7 | CREATE TABLE IF NOT EXISTS cloudkit_payments_metadata ( 8 | type INTEGER NOT NULL, 9 | id TEXT NOT NULL, 10 | unpadded_size INTEGER NOT NULL, 11 | record_creation INTEGER NOT NULL, 12 | record_blob BLOB NOT NULL, 13 | PRIMARY KEY (type, id) 14 | ); 15 | 16 | CREATE INDEX IF NOT EXISTS record_creation_idx 17 | ON cloudkit_payments_metadata(record_creation); 18 | 19 | CREATE TABLE IF NOT EXISTS cloudkit_payments_queue ( 20 | rowid INTEGER PRIMARY KEY, 21 | type INTEGER NOT NULL, 22 | id TEXT NOT NULL, 23 | date_added INTEGER NOT NULL 24 | ); -------------------------------------------------------------------------------- /phoenix-shared/src/commonTest/kotlin/fr/acinq/phoenix/utils/ConnectionTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.utils 18 | 19 | import fr.acinq.lightning.utils.Connection 20 | import plus 21 | import kotlin.test.Test 22 | import kotlin.test.assertEquals 23 | 24 | class ConnectionTests { 25 | @Test 26 | fun connectionPlusOperator() { 27 | assertEquals(Connection.ESTABLISHED, Connection.ESTABLISHED + Connection.ESTABLISHED) 28 | assertEquals(Connection.ESTABLISHING, Connection.ESTABLISHING + Connection.ESTABLISHING) 29 | assertEquals(Connection.CLOSED, Connection.CLOSED + Connection.CLOSED) 30 | 31 | assertEquals(Connection.ESTABLISHING, Connection.ESTABLISHED + Connection.ESTABLISHING) 32 | assertEquals(Connection.ESTABLISHING, Connection.ESTABLISHING + Connection.ESTABLISHED) 33 | assertEquals(Connection.ESTABLISHING, Connection.CLOSED + Connection.ESTABLISHING) 34 | assertEquals(Connection.ESTABLISHING, Connection.ESTABLISHING + Connection.CLOSED) 35 | 36 | assertEquals(Connection.CLOSED, Connection.ESTABLISHED + Connection.CLOSED) 37 | assertEquals(Connection.CLOSED, Connection.CLOSED + Connection.ESTABLISHED) 38 | } 39 | } -------------------------------------------------------------------------------- /phoenix-shared/src/iosMain/kotlin/fr/acinq/phoenix/data/iosElectrumRegtestConf.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.data 2 | 3 | actual fun platformElectrumRegtestConf(): ElectrumAddress = ElectrumAddress(host = "127.0.0.1", tcpPort = 51001, sslPort = 51002) 4 | -------------------------------------------------------------------------------- /phoenix-shared/src/iosMain/kotlin/fr/acinq/phoenix/db/SqlPaymentHooks.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.db 2 | 3 | import fr.acinq.bitcoin.ByteVector32 4 | import fr.acinq.lightning.utils.UUID 5 | import fr.acinq.lightning.utils.currentTimestampMillis 6 | import fr.acinq.phoenix.db.payments.CloudKitInterface 7 | import fracinqphoenixdb.Cloudkit_payments_queue 8 | 9 | fun Cloudkit_payments_queue.asPaymentRowId(): PaymentRowId? = when (type) { 10 | CloudKitRowType.INCOMING_PAYMENT.value -> { 11 | PaymentRowId.IncomingPaymentId(paymentHash = ByteVector32(id)) 12 | } 13 | CloudKitRowType.OUTGOING_PAYMENT.value -> { 14 | PaymentRowId.OutgoingPaymentId(id = UUID.fromString(id)) 15 | } 16 | else -> null 17 | } 18 | 19 | actual fun didCompletePaymentRow(id: PaymentRowId, database: PaymentsDatabase) { 20 | val now = currentTimestampMillis() 21 | val ckq = database.cloudKitPaymentsQueries 22 | ckq.addToQueue(type = id.db_type.value, id = id.db_id, date_added = now) 23 | } 24 | 25 | actual fun makeCloudKitDb(database: PaymentsDatabase): CloudKitInterface? { 26 | return CloudKitDb(database) 27 | } -------------------------------------------------------------------------------- /phoenix-shared/src/iosMain/kotlin/fr/acinq/phoenix/utils/platformIos.kt: -------------------------------------------------------------------------------- 1 | package fr.acinq.phoenix.utils 2 | 3 | import platform.Foundation.NSDocumentDirectory 4 | import platform.Foundation.NSSearchPathForDirectoriesInDomains 5 | import platform.Foundation.NSTemporaryDirectory 6 | import platform.Foundation.NSUserDomainMask 7 | 8 | 9 | actual class PlatformContext 10 | 11 | actual fun getApplicationFilesDirectoryPath(ctx: PlatformContext): String = 12 | NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0] as String 13 | 14 | actual fun getTemporaryDirectoryPath(ctx: PlatformContext): String = 15 | NSTemporaryDirectory() 16 | -------------------------------------------------------------------------------- /phoenix-shared/src/iosTest/kotlin/fr/acinq/phoenix/db/SqliteChannelsDatabaseTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 ACINQ SAS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fr.acinq.phoenix.db 18 | 19 | import com.squareup.sqldelight.db.SqlDriver 20 | import com.squareup.sqldelight.drivers.native.NativeSqliteDriver 21 | import fr.acinq.lightning.Lightning 22 | 23 | actual fun testChannelsDriver(): SqlDriver { 24 | return NativeSqliteDriver(ChannelsDatabase.Schema, ":memory:") 25 | } 26 | 27 | actual fun testPaymentsDriver(): SqlDriver { 28 | // In-memory databases don't seem to work on native/iOS. 29 | // The call succeeds, but in reality it creates a persistent database, 30 | // which then breaks our unit test logic. 31 | // return NativeSqliteDriver(PaymentsDatabase.Schema, ":memory:") 32 | // The docs reference other ways of making in-memory databases: 33 | // https://sqlite.org/inmemorydb.html 34 | // But none of them seem to work (at the time of writing). 35 | // 36 | // Current workaround is to create a fresh database for each test. 37 | val randomName = Lightning.randomBytes32().toHex() 38 | return NativeSqliteDriver(PaymentsDatabase.Schema, randomName) 39 | } 40 | 41 | // Workaround for known bugs in SQLDelight on native/iOS. 42 | actual fun isIOS(): Boolean { 43 | return true 44 | } 45 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | mavenCentral() 5 | maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev") 6 | } 7 | } 8 | 9 | rootProject.name = "phoenix-kmm" 10 | 11 | // We use a property defined in `local.properties` to know whether we should build the android application or not. 12 | // For example, iOS developers may want to skip that most of the time. 13 | val skipAndroid = File("$rootDir/local.properties").takeIf { it.exists() } 14 | ?.inputStream()?.use { java.util.Properties().apply { load(it) } } 15 | ?.run { getProperty("skip.android", "true")?.toBoolean() } 16 | ?: true 17 | 18 | // Use system properties to inject the property in other gradle build files. 19 | System.setProperty("includeAndroid", (!skipAndroid).toString()) 20 | 21 | include(":phoenix-shared") 22 | if (!skipAndroid) { 23 | include(":phoenix-android") 24 | } 25 | --------------------------------------------------------------------------------