Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/flipcash/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ dependencies {
implementation(project(":apps:flipcash:features:backupkey"))
implementation(project(":apps:flipcash:features:shareapp"))
implementation(project(":apps:flipcash:features:withdrawal"))
implementation(project(":apps:flipcash:features:onramp"))
implementation(project(":apps:flipcash:features:contact-verification"))
implementation(project(":apps:flipcash:features:tokens"))
implementation(project(":apps:flipcash:features:transactions"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import com.flipcash.app.login.router.LoginRouter
import com.flipcash.app.login.seed.SeedInputScreen
import com.flipcash.app.menu.MenuScreen
import com.flipcash.app.myaccount.MyAccountScreen
import com.flipcash.app.onramp.OnRampCustomAmountScreen
import com.flipcash.app.permissions.NotificationPermissionRationaleScreen
import com.flipcash.app.permissions.NotificationPermissionScreen
import com.flipcash.app.purchase.PurchaseAccountScreen
Expand Down Expand Up @@ -113,7 +112,6 @@ fun appEntryProvider(
annotatedEntry<AppRoute.Token.TxProcessing> { key ->
TokenTxProcessingScreen(key.swapId, key.swapPurpose, key.amount, key.awaitExternalWallet, key.isFundingShortfall)
}
annotatedEntry<AppRoute.Token.OnRamp> { key -> OnRampCustomAmountScreen(key.mint) }
annotatedEntry<AppRoute.Token.Discovery> { TokenDiscoveryScreen() }
annotatedEntry<AppRoute.Token.CurrencyCreator> { key ->
CurrencyCreatorFlowScreen(route = key, resultStateRegistry = resultStateRegistry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,15 @@ sealed interface AppRoute : NavKey, Parcelable {
val shortfall: Fiat? = null,
) : Token, FlowRouteWithResult<SwapResult> {
override val initialStack: List<NavKey>
get() = listOf(SwapStep.Entry(purpose))
get() = listOf(SwapStep.Entry(purpose, initialAmount = shortfall))
}

@Serializable
data object PhantomConnectInfo: Token

@Serializable
data object PhantomConfirmTransaction: Token

@Serializable
data class TxProcessing(
val swapId: SwapId,
Expand All @@ -149,9 +155,6 @@ sealed interface AppRoute : NavKey, Parcelable {
val isFundingShortfall: Boolean = false,
) : Token, NonDismissableRoute, NonDraggableRoute

@Serializable
data class OnRamp(val mint: Mint) : Token

@Serializable
data object Discovery: AppRoute

Expand Down Expand Up @@ -209,7 +212,7 @@ private fun buildVerificationInitialStack(
emailVerificationCode: String?,
): List<NavKey> {
if (includePhone && includeEmail) {
return listOf(VerificationStep.Intro(origin is AppRoute.Token.OnRamp))
return listOf(VerificationStep.Intro(origin is AppRoute.Token.Swap))
}
if (includePhone) {
return listOf(VerificationStep.PhoneEntry)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ fun Context.launchPhotos(searchQuery: String = "Flipcash") {
if (searchIntent.resolveActivity(packageManager) != null) {
startActivity(searchIntent)
} else {
println("No apps handling image search")
// Fall back to just opening the gallery
val photosIntent = IntentUtils.photosApp()
startActivity(photosIntent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@ import com.getcode.navigation.NonDismissableRoute
import com.getcode.navigation.NonDraggableRoute
import com.getcode.navigation.flow.FlowStep
import com.getcode.opencode.internal.solana.model.SwapId
import com.getcode.opencode.model.financial.Fiat
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Serializable
sealed interface SwapStep : FlowStep, Parcelable {
@Parcelize
@Serializable
data class Entry(val purpose: SwapPurpose) : SwapStep
data class Entry(val purpose: SwapPurpose, val initialAmount: Fiat? = null) : SwapStep

@Parcelize
@Serializable
data object SellReceipt : SwapStep

@Parcelize
@Serializable
data object PhantomConnect: SwapStep

@Parcelize
@Serializable
data object PhantomConfirmTransaction: SwapStep
@Parcelize
@Serializable
data class Processing(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,21 @@ import kotlinx.serialization.Serializable
* @see Buy Represents a purchase intent (e.g., swapping base currency for a specific token).
* @see Sell Represents a liquidation intent (e.g., swapping a specific token back to base currency).
*/
@Serializable
enum class FundingSource {
Flexible,
Phantom,
}

@Serializable
@Parcelize
sealed interface SwapPurpose : Parcelable {
val mint: Mint
sealed interface BalanceIncrease
sealed interface BalanceDecrease
@Serializable data class Buy(val mint: Mint) : SwapPurpose, BalanceIncrease
@Serializable data class FundWithWallet(val mint: Mint): SwapPurpose, BalanceIncrease
@Serializable data class Sell(val mint: Mint) : SwapPurpose, BalanceDecrease
@Serializable data class Buy(
override val mint: Mint,
val fundingSource: FundingSource = FundingSource.Flexible,
) : SwapPurpose, BalanceIncrease
@Serializable data class Sell(override val mint: Mint) : SwapPurpose, BalanceDecrease
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.flipcash.app.core.verification.email

import com.flipcash.app.core.AppRoute
import com.getcode.ed25519.Ed25519
import com.flipcash.app.core.tokens.SwapPurpose
import com.getcode.opencode.model.financial.Fiat
import com.getcode.opencode.utils.base64
import com.getcode.solana.keys.Mint
import com.getcode.solana.keys.base58
import com.getcode.utils.base58
import com.getcode.utils.decodeBase58
import com.getcode.utils.decodeBase64
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
Expand All @@ -24,7 +22,10 @@ sealed class EmailDeeplinkOrigin {
is OnRamp -> {
val amountString = amount?.let { Json.encodeToString(Fiat.Companion.serializer(), it) }
when (source) {
is AppRoute.Token.OnRamp -> "onramp|amountentry|${source.mint.base58()}"
is AppRoute.Token.Swap -> {
val mint = (source.purpose as? SwapPurpose.Buy)?.mint
"onramp|amountentry|${mint?.base58()}"
}
else -> "onramp|null|$amountString"
}
}
Expand All @@ -36,12 +37,8 @@ sealed class EmailDeeplinkOrigin {
companion object {
fun fromRoute(route: AppRoute?): EmailDeeplinkOrigin? {
return when (route) {
is AppRoute.Token.OnRamp -> {
OnRamp(route)
}

is AppRoute.Token.Swap -> OnRamp(route)
is AppRoute.Menu.MyAccount -> MyAccount

else -> null
}
}
Expand All @@ -53,18 +50,10 @@ sealed class EmailDeeplinkOrigin {
val source = when (splits[1]) {
"menu" -> AppRoute.Sheets.Menu
"amountentry" -> {
println("deeplink origin amountentry")
val mint = splits.getOrNull(2)?.let {
println("deeplink mint = $it")
Mint(it)
}

if (mint == null) {
println("deeplink mint is null")
return null
}
val mint = splits.getOrNull(2)?.let { Mint(it) }
?: return null

AppRoute.Token.OnRamp(mint)
AppRoute.Token.Swap(SwapPurpose.Buy(mint))
}
else -> null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!--
~ Copyright (C) 2026 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="278dp"
android:height="124dp"
android:viewportWidth="278"
android:viewportHeight="124">
<path
android:pathData="M61,12L61,12A50,50 0,0 1,111 62L111,62A50,50 0,0 1,61 112L61,112A50,50 0,0 1,11 62L11,62A50,50 0,0 1,61 12z"
android:fillColor="#AB9FF1"/>
<path
android:pathData="M37.77,87.76C45.63,87.76 51.53,80.93 55.06,75.53C54.63,76.72 54.39,77.92 54.39,79.07C54.39,82.22 56.2,84.47 59.77,84.47C64.67,84.47 69.91,80.17 72.63,75.53C72.44,76.2 72.34,76.82 72.34,77.39C72.34,79.59 73.58,80.98 76.1,80.98C84.05,80.98 92.05,66.88 92.05,54.55C92.05,44.95 87.2,36.49 75.01,36.49C53.58,36.49 30.49,62.68 30.49,79.59C30.49,86.23 34.06,87.76 37.77,87.76ZM67.63,53.5C67.63,51.11 68.96,49.44 70.91,49.44C72.82,49.44 74.15,51.11 74.15,53.5C74.15,55.89 72.82,57.61 70.91,57.61C68.96,57.61 67.63,55.89 67.63,53.5ZM77.82,53.5C77.82,51.11 79.15,49.44 81.1,49.44C83.01,49.44 84.34,51.11 84.34,53.5C84.34,55.89 83.01,57.61 81.1,57.61C79.15,57.61 77.82,55.89 77.82,53.5Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M139,54.42V62M139,62V69.58M139,62H131.42M139,62H146.58"
android:strokeAlpha="0.33"
android:strokeWidth="2.33333"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="square"/>
<path
android:pathData="M216.99,112C244.7,112 266.99,89.7 266.99,62C266.99,34.29 244.7,12 216.99,12C189.29,12 166.99,34.29 166.99,62C166.99,89.7 189.29,112 216.99,112Z"
android:fillColor="#2775CA"/>
<path
android:pathData="M226.58,27.62C226.79,26.79 227.63,26.37 228.46,26.79C239.92,30.54 249.08,39.5 252.83,51.37C259.08,71.16 248.25,92.2 228.46,98.45C228.25,98.66 227.83,98.66 227.63,98.66C226.79,98.45 226.38,97.83 226.38,96.99V94.08C226.38,93.03 226.79,92.41 227.63,91.99C236.17,88.87 243.04,82.2 246.17,73.45C252.21,57.41 243.88,39.29 227.63,33.46C227,33.04 226.38,32.21 226.38,31.37V28.46C226.38,28.04 226.38,27.83 226.58,27.62ZM206.38,26.58C207.21,26.79 207.63,27.41 207.63,28.25V31.16C207.63,32.21 207.21,32.83 206.38,33.25C197.83,36.37 190.96,43.04 187.83,51.79C181.79,67.83 190.13,85.95 206.38,91.78C207,92.2 207.63,93.03 207.63,93.66V96.58C207.63,96.99 207.63,97.2 207.42,97.41C207.21,98.24 206.38,98.66 205.54,98.24C193.88,94.49 184.92,85.53 181.17,73.87C174.92,54.08 185.75,33.04 205.54,26.79C205.75,26.58 206.17,26.58 206.38,26.58ZM218.46,37.42C219.5,37.62 220.12,38.25 220.12,39.08V44.08C224.91,44.5 228.66,48.04 229.5,52.62V52.83C229.5,53.66 228.88,54.29 228.04,54.29H224.71C224.08,54.29 223.46,53.87 223.25,53.25C222.21,50.12 220.12,48.87 216.37,48.87C212.21,48.87 210.12,50.75 210.12,53.66C210.12,56.58 211.38,58.25 217.62,59.08C226.37,60.12 230.75,62.62 230.75,69.91C230.75,75.54 226.58,79.91 220.33,80.95V85.95C220.13,86.99 219.5,87.62 218.67,87.62H215.54C214.5,87.41 213.87,86.79 213.87,85.95V80.95C207,79.91 203.67,76.16 202.83,70.95V70.75C202.83,69.91 203.46,69.29 204.29,69.29H207.83C208.46,69.29 209.08,69.7 209.29,70.54C209.92,73.66 211.79,75.95 217.21,75.95C221.17,75.95 224.08,73.66 224.08,70.33C224.08,67 222.21,65.75 216.37,64.71C207.62,63.66 203.46,60.96 203.46,54.08C203.46,48.87 207.42,44.71 213.67,43.87V39.08C213.88,38.04 214.5,37.42 215.33,37.42H218.46Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M258.73,104.76m-16.98,0a16.98,16.98 0,1 1,33.96 0a16.98,16.98 0,1 1,-33.96 0"
android:strokeWidth="3.95626"
android:fillColor="#ffffff"
android:strokeColor="#19191A"/>
<path
android:pathData="M267.23,108.22C267.49,108.22 267.62,108.53 267.44,108.71L264.67,111.48C264.56,111.59 264.41,111.65 264.26,111.65H250.23C250.18,111.65 250.12,111.63 250.07,111.6C250.02,111.57 249.99,111.52 249.97,111.47C249.95,111.42 249.94,111.36 249.95,111.3C249.96,111.25 249.99,111.2 250.03,111.16L252.8,108.39C252.91,108.28 253.05,108.22 253.21,108.22H267.23Z"
android:fillColor="#19191A"/>
<path
android:pathData="M264.26,103.01C264.41,103.01 264.56,103.07 264.67,103.18L267.44,105.95C267.62,106.13 267.49,106.44 267.23,106.44H253.21C253.05,106.44 252.91,106.38 252.8,106.27L250.03,103.5C249.99,103.46 249.96,103.41 249.95,103.35C249.94,103.3 249.95,103.24 249.97,103.19C249.99,103.14 250.02,103.09 250.07,103.06C250.12,103.03 250.18,103.01 250.23,103.01H264.26Z"
android:fillColor="#19191A"/>
<path
android:pathData="M267.23,97.87C267.49,97.87 267.62,98.18 267.44,98.36L264.67,101.13C264.56,101.24 264.41,101.3 264.26,101.3H250.23C250.18,101.3 250.12,101.28 250.07,101.25C250.02,101.22 249.99,101.18 249.97,101.12C249.95,101.07 249.94,101.01 249.95,100.96C249.96,100.9 249.99,100.85 250.03,100.81L252.8,98.04L252.8,98.04C252.91,97.93 253.06,97.87 253.21,97.87H267.23Z"
android:fillColor="#19191A"/>
</vector>
35 changes: 35 additions & 0 deletions apps/flipcash/core/src/main/res/drawable/ic_phantom_connected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!--
~ Copyright (C) 2026 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="111dp"
android:height="112dp"
android:viewportWidth="111"
android:viewportHeight="112">
<path
android:pathData="M50,100C77.71,100 100,77.71 100,50C100,22.29 77.71,0 50,0C22.29,0 0,22.29 0,50C0,77.71 22.29,100 50,100Z"
android:fillColor="#AB9FF1"/>
<path
android:pathData="M26.77,75.76C34.63,75.76 40.53,68.93 44.06,63.53C43.63,64.72 43.39,65.92 43.39,67.07C43.39,70.22 45.2,72.47 48.77,72.47C53.67,72.47 58.91,68.17 61.63,63.53C61.44,64.2 61.34,64.82 61.34,65.39C61.34,67.59 62.58,68.98 65.1,68.98C73.05,68.98 81.05,54.88 81.05,42.55C81.05,32.95 76.2,24.49 64.01,24.49C42.58,24.49 19.49,50.68 19.49,67.59C19.49,74.23 23.06,75.76 26.77,75.76ZM56.63,41.5C56.63,39.11 57.96,37.44 59.91,37.44C61.82,37.44 63.15,39.11 63.15,41.5C63.15,43.89 61.82,45.61 59.91,45.61C57.96,45.61 56.63,43.89 56.63,41.5ZM66.82,41.5C66.82,39.11 68.15,37.44 70.1,37.44C72.01,37.44 73.34,39.11 73.34,41.5C73.34,43.89 72.01,45.61 70.1,45.61C68.15,45.61 66.82,43.89 66.82,41.5Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M91.73,92.76m-16.98,0a16.98,16.98 0,1 1,33.96 0a16.98,16.98 0,1 1,-33.96 0"
android:strokeWidth="3.95626"
android:fillColor="#ffffff"
android:strokeColor="#19191A"/>
<path
android:pathData="M99.3,88.29L89.72,99.05L84.69,94.11L86.15,92.63L89.62,96.03L97.75,86.9L99.3,88.29Z"
android:fillColor="#19191A"/>
</vector>
15 changes: 15 additions & 0 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,14 @@
<string name="title_onrampProviderBackpack">Backpack Wallet</string>
<string name="title_onrampProviderPhantom">Phantom Wallet</string>
<string name="title_onrampProviderSolflare">Solflare Wallet</string>
<string name="title_onrampProviderOtherWallet">Other Wallet</string>

<string name="action_addCashWithGooglePay">Add Cash with Google Pay</string>
<string name="action_addCashWithDebitCard">Add Cash with Debit Card</string>
<string name="action_addCashWithCreditCard">Add Cash with Credit Card</string>
<string name="label_confirmIn">Confirm In</string>
<string name="label_phantom">Phantom</string>

<string name="label_solflare">Solflare</string>
<string name="label_backpack">Backpack</string>

Expand Down Expand Up @@ -439,6 +441,7 @@
<string name="title_amountToBuy">Amount to Buy</string>
<string name="title_amountToSell">Amount to Sell</string>
<string name="subtitle_buySellCashHint">Enter up to %1$s</string>
<string name="subtitle_buyHintBelowMinimum">You must buy at least %1$s</string>
<string name="subtitle_buyHintLimitExceeded">You can only buy up to %1$s</string>
<string name="subtitle_sellHintLimitExceeded">You can only sell up to %1$s</string>
<string name="action_buy">Buy</string>
Expand Down Expand Up @@ -518,6 +521,9 @@
<string name="title_settingsSectionAccount">Account</string>
<string name="title_userFlags">User Flags</string>

<string name="title_purchase">Purchase</string>
<string name="action_connectYourPhantomWallet">Connect Your Phantom Wallet</string>

<string name="error_title_onrampAmountTooLow">$5 Minimum Purchase</string>
<string name="error_description_onrampAmountTooLow">Please enter an amount of $5 or higher</string>

Expand Down Expand Up @@ -640,4 +646,13 @@
<string name="title_leaderboard">Leaderboard</string>

<string name="action_discover">Discover</string>

<string name="title_buyWithPhantom">Buy With Phantom</string>
<string name="description_buyWithPhantom">Purchase using Solana USDC in Phantom. Simply connect your wallet and confirm the transaction</string>

<string name="title_confirmation">Confirmation</string>
<string name="title_buyWithPhantomConnected">Connected</string>
<string name="description_buyWithPhantomConnected">Confirm the transaction in Phantom to continue</string>

<string name="title_depositToken">Deposit %1$s</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flipcash.app.cash.internal
import androidx.lifecycle.viewModelScope
import com.flipcash.app.core.AppRoute
import com.flipcash.app.core.bill.Bill
import com.flipcash.app.core.tokens.SwapPurpose
import com.flipcash.app.core.ui.CurrencyHolder
import com.flipcash.app.tokens.TokenCoordinator
import com.flipcash.features.cash.R
Expand Down Expand Up @@ -338,12 +339,12 @@ internal class CashScreenViewModel @Inject constructor(
.filterIsInstance<Event.AddCashToWallet>()
.map { it.amount }
.onEach { shortfall ->
// route to buy the token
println("shortfall=$shortfall")
// route directly to the swap amount screen, skipping token info
val mint = stateFlow.value.selectedTokenAddress!!
dispatchEvent(
Event.OpenScreen(
AppRoute.Token.Info(
mint = stateFlow.value.selectedTokenAddress!!,
AppRoute.Token.Swap(
purpose = SwapPurpose.Buy(mint),
shortfall = shortfall,
),
)
Expand Down
Loading
Loading