diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/BillState.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/BillState.kt index 0d4bf9aae..55d11b06d 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/BillState.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/BillState.kt @@ -5,16 +5,13 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import com.flipcash.core.R -import com.getcode.opencode.internal.manager.VerifiedState import com.getcode.opencode.model.financial.Fiat import com.getcode.opencode.model.financial.LocalFiat import com.getcode.opencode.model.financial.Token -import com.getcode.opencode.model.financial.usdf -import com.getcode.solana.keys.Mint import kotlin.time.Duration data class BillState( - val bill: Bill?, + val bill: Scannable.Payable?, val showToast: Boolean, val toast: BillToast?, val valuation: Valuation?, @@ -86,51 +83,6 @@ data class BillState( } } -sealed interface Bill { - val didReceive: Boolean - val confirmationDelay: Duration - val token: Token - val amount: LocalFiat - val data: List - val disableGestures: Boolean - - enum class Kind { - cash, airdrop - } - - val canSwipeToDismiss: Boolean - get() = when (this) { - is Cash -> !disableGestures - } - - val canFlip: Boolean - - val metadata: Metadata - get() { - return when (this) { - is Cash -> Metadata( - token = token, - amount = amount, - data = data - ) - } - } - - data class Cash( - override val token: Token, - override val amount: LocalFiat, - override val didReceive: Boolean = false, - override val disableGestures: Boolean = false, - override val confirmationDelay: Duration = Duration.ZERO, - override val data: List = emptyList(), - val kind: Kind = Kind.cash, - val verifiedState: VerifiedState? = null, - val nonce: List = emptyList(), - val renderAsBill: Boolean = token.address != Mint.usdf, - ) : Bill { - override val canFlip: Boolean = false - } -} sealed interface Valuation data class PaymentValuation(val amount: Fiat): Valuation diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/Scannable.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/Scannable.kt new file mode 100644 index 000000000..1ead1dad9 --- /dev/null +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/bill/Scannable.kt @@ -0,0 +1,99 @@ +package com.flipcash.app.core.bill + +import com.getcode.opencode.internal.manager.VerifiedState +import com.getcode.opencode.model.financial.LocalFiat +import com.getcode.opencode.model.financial.Token +import com.getcode.solana.keys.Mint +import kotlin.time.Duration + +/** + * Anything that renders a scannable code. The single common member is [data] — + * the encoded KikCode bytes (an `OpenCodePayload.codeData`). + */ +sealed interface Scannable { + val data: List + + /** + * A payment-bearing scannable (carries a token + amount). [CashBill] and [GoldBar] + * differ only in which renderer draws them; the type IS the render choice. + */ + sealed interface Payable : Scannable { + val token: Token + val amount: LocalFiat + val didReceive: Boolean + val disableGestures: Boolean + val confirmationDelay: Duration + val kind: Kind + val verifiedState: VerifiedState? + val nonce: List + + enum class Kind { cash, airdrop } + + val canSwipeToDismiss: Boolean get() = !disableGestures + val canFlip: Boolean get() = false + val metadata: Metadata get() = Metadata(token = token, amount = amount, data = data) + + /** Returns a copy of this bill with its scannable [code] (+ [nonce]) stamped in. */ + fun stamped(code: List, nonce: List): Payable + + companion object { + /** Applies the USDF->[GoldBar] rule; every other token -> [CashBill]. */ + fun forToken( + token: Token, + amount: LocalFiat, + didReceive: Boolean = false, + disableGestures: Boolean = false, + confirmationDelay: Duration = Duration.ZERO, + data: List = emptyList(), + kind: Kind = Kind.cash, + verifiedState: VerifiedState? = null, + nonce: List = emptyList(), + ): Payable = if (token.address == Mint.usdf) { + GoldBar( + token = token, amount = amount, didReceive = didReceive, + disableGestures = disableGestures, confirmationDelay = confirmationDelay, + data = data, kind = kind, verifiedState = verifiedState, nonce = nonce, + ) + } else { + CashBill( + token = token, amount = amount, didReceive = didReceive, + disableGestures = disableGestures, confirmationDelay = confirmationDelay, + data = data, kind = kind, verifiedState = verifiedState, nonce = nonce, + ) + } + } + } + + data class CashBill( + override val token: Token, + override val amount: LocalFiat, + override val didReceive: Boolean = false, + override val disableGestures: Boolean = false, + override val confirmationDelay: Duration = Duration.ZERO, + override val data: List = emptyList(), + override val kind: Payable.Kind = Payable.Kind.cash, + override val verifiedState: VerifiedState? = null, + override val nonce: List = emptyList(), + ) : Payable { + override fun stamped(code: List, nonce: List) = copy(data = code, nonce = nonce) + } + + data class GoldBar( + override val token: Token, + override val amount: LocalFiat, + override val didReceive: Boolean = false, + override val disableGestures: Boolean = false, + override val confirmationDelay: Duration = Duration.ZERO, + override val data: List = emptyList(), + override val kind: Payable.Kind = Payable.Kind.cash, + override val verifiedState: VerifiedState? = null, + override val nonce: List = emptyList(), + ) : Payable { + override fun stamped(code: List, nonce: List) = copy(data = code, nonce = nonce) + } + + data class TipCard( + override val data: List, + val username: String, + ) : Scannable +} diff --git a/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/BillStateTest.kt b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/BillStateTest.kt index 907367024..db8fb1402 100644 --- a/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/BillStateTest.kt +++ b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/BillStateTest.kt @@ -65,7 +65,7 @@ class BillStateTest { @Test fun `canSwipeToDismiss returns true when bill allows gestures`() { - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = testToken(), amount = testLocalFiat(), disableGestures = false, @@ -77,7 +77,7 @@ class BillStateTest { @Test fun `canSwipeToDismiss returns false when bill disableGestures is true`() { - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = testToken(), amount = testLocalFiat(), disableGestures = true, @@ -100,7 +100,7 @@ class BillStateTest { @Test fun `confirmationDelayMillis converts Duration correctly`() { - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = testToken(), amount = testLocalFiat(), confirmationDelay = 3.seconds, @@ -119,7 +119,7 @@ class BillStateTest { // endregion - // region Bill.Cash metadata and canFlip + // region Scannable.CashBill metadata and canFlip @Test fun `Bill Cash metadata extracts token and amount`() { @@ -127,7 +127,7 @@ class BillStateTest { val amount = testLocalFiat() val data = listOf(1, 2, 3) - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = token, amount = amount, data = data, @@ -142,7 +142,7 @@ class BillStateTest { @Test fun `Bill Cash canFlip is always false`() { - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = testToken(), amount = testLocalFiat(), ) diff --git a/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/ScannableTest.kt b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/ScannableTest.kt new file mode 100644 index 000000000..38d371be8 --- /dev/null +++ b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/bill/ScannableTest.kt @@ -0,0 +1,103 @@ +package com.flipcash.app.core.bill + +import com.getcode.opencode.model.financial.Fiat +import com.getcode.opencode.model.financial.HolderMetrics +import com.getcode.opencode.model.financial.LocalFiat +import com.getcode.opencode.model.financial.MintMetadata +import com.getcode.opencode.model.financial.Rate +import com.getcode.opencode.model.financial.Token +import com.getcode.opencode.model.financial.VmMetadata +import com.getcode.solana.keys.Mint +import com.getcode.solana.keys.PublicKey +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.seconds + +class ScannableTest { + + private fun tokenWith(mint: Mint): Token = MintMetadata( + address = mint, + decimals = 6, + name = "Test", + symbol = "TST", + createdAt = null, + description = "", + imageUrl = "", + vmMetadata = VmMetadata( + vm = PublicKey.fromBase58("11111111111111111111111111111111"), + authority = PublicKey.fromBase58("11111111111111111111111111111111"), + lockDurationInDays = 21 + ), + launchpadMetadata = null, + billCustomizations = null, + socialLinks = emptyList(), + holderMetrics = HolderMetrics.None, + ) + + private fun localFiat(): LocalFiat = LocalFiat( + underlyingTokenAmount = Fiat(5.0), + nativeAmount = Fiat(5.0), + rate = Rate.oneToOne, + mint = Mint.usdf, + ) + + private val nonUsdfMint = Mint("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") // USDC — not USDF + + @Test + fun `forToken returns GoldBar for usdf mint`() { + val bill = Scannable.Payable.forToken(token = tokenWith(Mint.usdf), amount = localFiat()) + assertIs(bill) + } + + @Test + fun `forToken returns CashBill for non-usdf mint`() { + val bill = Scannable.Payable.forToken(token = tokenWith(nonUsdfMint), amount = localFiat()) + assertIs(bill) + } + + @Test + fun `forToken preserves passed-through fields`() { + val expectedData = listOf(1, 2, 3) + val expectedNonce = listOf(9) + val expectedDelay = 4.seconds + val bill = Scannable.Payable.forToken( + token = tokenWith(nonUsdfMint), + amount = localFiat(), + didReceive = true, + disableGestures = true, + confirmationDelay = expectedDelay, + data = expectedData, + kind = Scannable.Payable.Kind.airdrop, + nonce = expectedNonce, + ) + assertTrue(bill.didReceive) + assertTrue(bill.disableGestures) + assertEquals(expectedDelay, bill.confirmationDelay) + assertEquals(expectedData, bill.data) + assertEquals(Scannable.Payable.Kind.airdrop, bill.kind) + assertEquals(expectedNonce, bill.nonce) + } + + @Test + fun `stamped replaces code and nonce on CashBill and keeps its type`() { + val bill = Scannable.CashBill(token = tokenWith(nonUsdfMint), amount = localFiat()) + val code = listOf(1, 2, 3) + val nonce = listOf(9, 8) + val stamped = bill.stamped(code = code, nonce = nonce) + assertIs(stamped) + assertEquals(code, stamped.data) + assertEquals(nonce, stamped.nonce) + assertEquals(bill.token, stamped.token) + } + + @Test + fun `stamped keeps GoldBar type`() { + val bar = Scannable.GoldBar(token = tokenWith(Mint.usdf), amount = localFiat()) + val stamped = bar.stamped(code = listOf(7), nonce = emptyList()) + assertIs(stamped) + assertEquals(listOf(7), stamped.data) + assertEquals(bar.token, stamped.token) + } +} diff --git a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillCustomizationScaffold.kt b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillCustomizationScaffold.kt index 53554b1fa..720d2ea37 100644 --- a/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillCustomizationScaffold.kt +++ b/apps/flipcash/features/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillCustomizationScaffold.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flipcash.app.bill.customization.components.BillPlayground import com.flipcash.app.bills.AnimatedBill -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.features.bill.playground.R import com.getcode.theme.CodeTheme import com.getcode.ui.components.AppBarDefaults @@ -82,7 +82,6 @@ fun BillPlaygroundScaffold(content: @Composable () -> Unit) { derivedStateOf { if (!playgroundState.context.renderAsOverlay) return@derivedStateOf null val bill = playgroundState.bill ?: return@derivedStateOf null - if (bill !is Bill.Cash) return@derivedStateOf null bill.copy( token = bill.token.copy( billCustomizations = customizationsOptions diff --git a/apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt b/apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt index f0d29752d..6b1f02a30 100644 --- a/apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt +++ b/apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt @@ -4,7 +4,7 @@ import androidx.lifecycle.viewModelScope import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.AppRoute -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.tokens.SwapPurpose import com.flipcash.app.core.ui.CurrencyHolder import com.flipcash.app.tokens.TokenCoordinator @@ -105,7 +105,7 @@ internal class CashScreenViewModel @Inject constructor( data class OnCurrencyChanged(val model: com.getcode.opencode.model.financial.Currency) : Event data class OnLimitsChanged(val limits: Limits?) : Event data object OnGive : Event - data class PresentBill(val bill: Bill.Cash) : Event + data class PresentBill(val bill: Scannable.Payable) : Event data class AddCashToWallet(val amount: Fiat) : Event data class UpdateLoadingState(val loading: Boolean = false, val success: Boolean = false) : @@ -260,7 +260,7 @@ internal class CashScreenViewModel @Inject constructor( return@onEach } - val bill = Bill.Cash( + val bill = Scannable.Payable.forToken( token = stateFlow.value.token!!.token, amount = result.localFiat, verifiedState = result.verifiedState, diff --git a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/CurrencyCreatorViewModel.kt b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/CurrencyCreatorViewModel.kt index f0b7028f6..5d9cff40f 100644 --- a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/CurrencyCreatorViewModel.kt +++ b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/CurrencyCreatorViewModel.kt @@ -4,7 +4,7 @@ import android.net.Uri import androidx.compose.foundation.text.input.TextFieldState import androidx.core.text.trimmedLength import androidx.lifecycle.viewModelScope -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.data.Loadable import com.flipcash.app.core.extensions.flatMapResult import com.flipcash.app.core.extensions.onResult @@ -109,12 +109,12 @@ internal class CurrencyCreatorViewModel @Inject constructor( val descriptionFieldState: TextFieldState = TextFieldState(), val icon: Loadable = Loadable.Loading(), val customizations: TokenBillCustomizations? = null, - val bill: Bill? = null, + val bill: Scannable.Payable? = null, val createdMint: Mint? = null, val launchedToken: Token? = null, // The celebratory give-bill for the freshly launched currency, priced through the // token's bonding curve (not USDF 1:1) and carrying the verified state a grab needs. - val launchBill: Bill.Cash? = null, + val launchBill: Scannable.Payable? = null, val purchaseAmount: Fiat = 5.toFiat(), val feeAmount: Fiat? = null, val processingState: LoadingSuccessState = LoadingSuccessState(), @@ -169,7 +169,7 @@ internal class CurrencyCreatorViewModel @Inject constructor( data class OnPurchaseAmountChanged(val amount: Fiat, val feeAmount: Fiat) : Event - data class OnBillConfirmed(val bill: Bill?) : Event + data class OnBillConfirmed(val bill: Scannable.Payable?) : Event data class UpdateProcessingState( val loading: Boolean = false, val success: Boolean = false, @@ -183,7 +183,7 @@ internal class CurrencyCreatorViewModel @Inject constructor( data class ConfirmPurchase(val fundedWith: Mint): Event data class PurchaseSubmitted(val swapId: SwapId, val mint: Mint) : Event - data class PurchaseCompleted(val token: Token, val bill: Bill.Cash?): Event + data class PurchaseCompleted(val token: Token, val bill: Scannable.Payable?): Event data object OnIntroContinue : Event data object AdvanceFromInfo : Event @@ -392,7 +392,7 @@ internal class CurrencyCreatorViewModel @Inject constructor( is ImageModerationError.Denied -> { BottomBarManager.showAlert( title = resources.getString(R.string.error_title_imageNotAllowed), - message = resources.getString(R.string.error_description_nameNotAllowed) + message = resources.getString(R.string.error_description_imageNotAllowed) ) } @@ -630,7 +630,7 @@ internal class CurrencyCreatorViewModel @Inject constructor( * * Returns null if the verified fiat can't be computed; the launch still succeeds. */ - private suspend fun buildLaunchBill(token: Token): Bill.Cash? { + private suspend fun buildLaunchBill(token: Token): Scannable.Payable? { val balance = tokenCoordinator.tokenBalances.firstOrNull() ?.firstOrNull { it.token.address == token.address } ?.balance @@ -641,7 +641,7 @@ internal class CurrencyCreatorViewModel @Inject constructor( rate = exchange.preferredRate, ).getOrNull() ?: return null - return Bill.Cash( + return Scannable.Payable.forToken( token = token, amount = verifiedFiat.localFiat, verifiedState = verifiedFiat.verifiedState, diff --git a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/screens/BillCustomizationScreen.kt b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/screens/BillCustomizationScreen.kt index 497f2959e..0feaa046d 100644 --- a/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/screens/BillCustomizationScreen.kt +++ b/apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/screens/BillCustomizationScreen.kt @@ -19,7 +19,7 @@ import com.flipcash.app.bill.customization.LocalBillPlaygroundController import com.flipcash.app.bill.customization.PlaygroundContext import com.flipcash.app.bill.customization.components.BillPlayground import com.flipcash.app.bills.RenderedBill -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.ui.transitions.SharedTransition import com.flipcash.app.core.ui.transitions.sharedBoundsTransition import com.flipcash.app.currencycreator.internal.CurrencyCreatorViewModel @@ -71,7 +71,6 @@ internal fun BillCustomizationContent( ) { derivedStateOf { val bill = playgroundState.bill ?: return@derivedStateOf null - if (bill !is Bill.Cash) return@derivedStateOf null bill.copy( token = bill.token.copy( billCustomizations = state.customizations @@ -97,7 +96,7 @@ internal fun BillCustomizationContent( .sharedBoundsTransition( transition = SharedTransition.CurrencyBill ), - bill = bill as Bill, + bill = bill, ) } diff --git a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/BillContainerView.kt b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/BillContainerView.kt index 81db456c0..f4fe75ffe 100644 --- a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/BillContainerView.kt +++ b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/BillContainerView.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flipcash.app.bills.AnimatedBill import com.flipcash.app.core.android.extensions.launchAppSettings -import com.flipcash.app.core.bill.Bill import com.flipcash.app.scanner.internal.ScannerDecorItem import com.flipcash.app.scanner.internal.ui.components.DecorView import com.flipcash.app.scanner.internal.ui.modals.ReceivedFundsConfirmation diff --git a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/ReceivedFundsConfirmation.kt b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/ReceivedFundsConfirmation.kt index 14d74e108..92c70bf4c 100644 --- a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/ReceivedFundsConfirmation.kt +++ b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/ReceivedFundsConfirmation.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.money.formatted import com.flipcash.app.core.ui.TokenIconWithName import com.flipcash.features.scanner.R @@ -24,7 +24,7 @@ import com.getcode.ui.theme.CodeButton @Composable internal fun ReceivedFundsConfirmation( - bill: Bill, + bill: Scannable.Payable, onClaim: () -> Unit, ) { val exchange = LocalExchange.current diff --git a/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillPlaygroundController.kt b/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillPlaygroundController.kt index 53f04bf35..bab550869 100644 --- a/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillPlaygroundController.kt +++ b/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/BillPlaygroundController.kt @@ -6,7 +6,7 @@ import com.flipcash.app.bill.customization.internal.features.BlendMode import com.flipcash.app.bill.customization.internal.features.ColorState import com.flipcash.app.bill.customization.internal.features.GraphicState import com.flipcash.app.bill.customization.models.PlaygroundFeature -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.getcode.opencode.model.financial.Fiat import com.getcode.opencode.model.financial.Token import com.getcode.opencode.model.financial.toFiat @@ -29,7 +29,7 @@ interface BillPlaygroundController { } data class PlaygroundState( - val bill: Bill? = null, + val bill: Scannable.CashBill? = null, val context: PlaygroundContext = PlaygroundContext.Standalone, val features: List = PlaygroundFeature.entries.toList(), val selectedFeature: PlaygroundFeature = PlaygroundFeature.Background, @@ -79,10 +79,9 @@ data class PlaygroundState( icon = null, ) - val customizedBill: Bill? + val customizedBill: Scannable.CashBill? get() { if (bill == null) return null - if (bill !is Bill.Cash) return null return bill.copy( token = bill.token.copy( billCustomizations = customizations diff --git a/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/internal/InternalBillPlaygroundController.kt b/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/internal/InternalBillPlaygroundController.kt index 87619d7dc..a0dcf401a 100644 --- a/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/internal/InternalBillPlaygroundController.kt +++ b/apps/flipcash/shared/bill-customization/src/main/kotlin/com/flipcash/app/bill/customization/internal/InternalBillPlaygroundController.kt @@ -13,7 +13,7 @@ import com.flipcash.app.bill.customization.internal.features.ColorState import com.flipcash.app.bill.customization.internal.features.GraphicState import com.flipcash.app.bill.customization.internal.features.TextureController import com.flipcash.app.bill.customization.models.PlaygroundFeature -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.featureflags.FeatureFlagController import com.getcode.opencode.model.core.OpenCodePayload import com.getcode.opencode.model.core.PayloadKind @@ -135,12 +135,11 @@ class InternalBillPlaygroundController( nonce = nonce ) // create bill for token - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = token.copy(billCustomizations = customizations), amount = demoAmount, disableGestures = true, data = payloadInfo.codeData.toList(), - renderAsBill = true, ) _state.update { current -> diff --git a/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/AnimatedBill.kt b/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/AnimatedBill.kt index d452cdc71..aee3aaa18 100644 --- a/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/AnimatedBill.kt +++ b/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/AnimatedBill.kt @@ -14,7 +14,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.getcode.theme.CodeTheme import com.getcode.ui.theme.CustomSwipeToDismiss @@ -28,9 +28,9 @@ fun AnimatedBill( ), dismissState: DismissState, dismissed: Boolean, - transitionSpec: AnimatedContentTransitionScope.() -> ContentTransform, - contentKey: (Bill?) -> Any? = { it }, - bill: Bill?, + transitionSpec: AnimatedContentTransitionScope.() -> ContentTransform, + contentKey: (Scannable.Payable?) -> Any? = { it }, + bill: Scannable.Payable?, ) { AnimatedContent( modifier = modifier, diff --git a/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/RenderedBill.kt b/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/RenderedBill.kt index ab26b951c..b7378da57 100644 --- a/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/RenderedBill.kt +++ b/apps/flipcash/shared/bills/src/main/kotlin/com/flipcash/app/bills/RenderedBill.kt @@ -4,42 +4,32 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.flipcash.app.core.bill.Bill -import com.getcode.opencode.model.core.OpenCodePayload -import com.getcode.opencode.model.core.PayloadKind +import com.flipcash.app.core.bill.Scannable import com.getcode.opencode.model.financial.CurrencyCode import com.getcode.opencode.model.financial.Fiat import com.getcode.opencode.model.financial.LocalFiat import com.getcode.opencode.model.financial.Rate import com.getcode.opencode.model.financial.Token -import com.getcode.opencode.model.financial.usdf -import com.getcode.solana.keys.Mint import com.getcode.theme.CodeTheme import com.getcode.theme.DesignSystem @Composable fun RenderedBill( modifier: Modifier = Modifier, - bill: Bill, + bill: Scannable.Payable, ) { when (bill) { - is Bill.Cash -> { - if (bill.renderAsBill) { - CashBill( - modifier = modifier, - payloadData = bill.data, - amount = bill.amount, - token = bill.token - ) - } else { - GoldBar( - modifier = modifier - .padding(horizontal = CodeTheme.dimens.inset), - payloadData = bill.data, - amount = bill.amount.underlyingTokenAmount, - ) - } - } + is Scannable.CashBill -> CashBill( + modifier = modifier, + payloadData = bill.data, + amount = bill.amount, + token = bill.token + ) + is Scannable.GoldBar -> GoldBar( + modifier = modifier.padding(horizontal = CodeTheme.dimens.inset), + payloadData = bill.data, + amount = bill.amount.underlyingTokenAmount, + ) } } diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt index 747e5c214..e0c059fdd 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt @@ -1,8 +1,8 @@ package com.flipcash.app.session import androidx.compose.runtime.staticCompositionLocalOf -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.session.BillDeterminationResult.ActedUpon import com.getcode.opencode.model.financial.Token import com.flipcash.app.core.AppRoute @@ -20,7 +20,7 @@ data object PutInWallet : BillDeterminationResult, ActedUpon interface BillOperations { val billState: StateFlow - fun showBill(bill: Bill) + fun showBill(bill: Scannable.Payable) fun dismissBill(action: BillDeterminationResult) } diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegate.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegate.kt index 3e9dae7c6..5f4d08d53 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegate.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegate.kt @@ -2,8 +2,8 @@ package com.flipcash.app.session.internal.delegates import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.bill.PaymentValuation import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.core.internal.errors.showNetworkError @@ -66,7 +66,7 @@ class BillPresentationDelegate @Inject constructor( ) : BillOperations { sealed interface Event { - data class SendAsLinkRequested(val bill: Bill.Cash, val owner: AccountCluster) : Event + data class SendAsLinkRequested(val bill: Scannable.Payable, val owner: AccountCluster) : Event data object RefreshFeed : Event } @@ -77,7 +77,7 @@ class BillPresentationDelegate @Inject constructor( override val billState: StateFlow get() = billController.state - override fun showBill(bill: Bill) { + override fun showBill(bill: Scannable.Payable) { if (bill.amount.nativeAmount.decimalValue == 0.0) return val owner = userManager.accountCluster ?: return @@ -85,44 +85,29 @@ class BillPresentationDelegate @Inject constructor( return ErrorUtils.showNetworkError(resources) } - when (bill) { - is Bill.Cash -> { - when (bill.kind) { - Bill.Kind.airdrop -> { - billController.update { - it.copy( - primaryAction = null, - secondaryAction = null, - ) - } - } - - Bill.Kind.cash -> { - if (bill.didReceive) { - billController.update { - it.copy( - primaryAction = null, - secondaryAction = null, - ) - } - } else { - billController.update { - it.copy( - primaryAction = BillState.Action.SendAsLink( - action = { - billController.cancelAwaitForGrab() - _events.trySend(Event.SendAsLinkRequested(bill, owner)) - } - ), - secondaryAction = BillState.Action.Cancel( - action = { dismissBill(PutInWallet) } - ), - ) - } - } - awaitBillGrab(bill, owner) + when (bill.kind) { + Scannable.Payable.Kind.airdrop -> { + billController.update { it.copy(primaryAction = null, secondaryAction = null) } + } + Scannable.Payable.Kind.cash -> { + if (bill.didReceive) { + billController.update { it.copy(primaryAction = null, secondaryAction = null) } + } else { + billController.update { + it.copy( + primaryAction = BillState.Action.SendAsLink( + action = { + billController.cancelAwaitForGrab() + _events.trySend(Event.SendAsLinkRequested(bill, owner)) + } + ), + secondaryAction = BillState.Action.Cancel( + action = { dismissBill(PutInWallet) } + ), + ) } } + awaitBillGrab(bill, owner) } } } @@ -135,13 +120,13 @@ class BillPresentationDelegate @Inject constructor( } } - internal fun awaitBillGrab(bill: Bill, owner: AccountCluster) { + internal fun awaitBillGrab(bill: Scannable.Payable, owner: AccountCluster) { analytics.transferStart(Analytics.Transfer.Initiate.GiveBillStart) billController.awaitGrab( amount = bill.amount, token = bill.token, - verifiedState = (bill as? Bill.Cash)?.verifiedState, - nonce = (bill as? Bill.Cash)?.nonce?.takeIf { it.isNotEmpty() }, + verifiedState = bill.verifiedState, + nonce = bill.nonce.takeIf { it.isNotEmpty() }, owner = owner, onGrabbed = { amount -> tokenCoordinator.subtract(bill.token, amount) @@ -187,15 +172,10 @@ class BillPresentationDelegate @Inject constructor( ) } - private fun presentBillToUser(data: List, nonce: List, bill: Bill) { + private fun presentBillToUser(data: List, nonce: List, bill: Scannable.Payable) { if (billController.state.value.bill != null) return - val presentedBill = when (bill) { - is Bill.Cash -> bill.copy( - data = data, - nonce = nonce, - ) - } + val presentedBill = bill.stamped(code = data, nonce = nonce) billController.update { it.copy( diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CashLinkDelegate.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CashLinkDelegate.kt index f3e1e8cf6..a0213e2f1 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CashLinkDelegate.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CashLinkDelegate.kt @@ -2,7 +2,7 @@ package com.flipcash.app.session.internal.delegates import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.core.navigation.DeeplinkType import com.flipcash.app.session.CashLinkOperations @@ -48,7 +48,7 @@ class CashLinkDelegate @Inject constructor( ) : CashLinkOperations { sealed interface Event { - data class BillReady(val bill: Bill) : Event + data class BillReady(val bill: Scannable.Payable) : Event data object RefreshFeed : Event data object CheckPendingFeed : Event } @@ -128,7 +128,7 @@ class CashLinkDelegate @Inject constructor( tokenCoordinator.add(token, amount) giftCardClaimInProgress.value = null analytics.transfer(Analytics.Transfer.ClaimedCashLink, amount = amount) - val bill = Bill.Cash( + val bill = Scannable.Payable.forToken( amount = amount, token = token, didReceive = true, diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CodeScanDelegate.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CodeScanDelegate.kt index b85efdc2e..670f48b46 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CodeScanDelegate.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/CodeScanDelegate.kt @@ -2,7 +2,7 @@ package com.flipcash.app.session.internal.delegates import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.CodeScanOperations import com.flipcash.app.session.internal.SessionStateHolder @@ -51,7 +51,7 @@ class CodeScanDelegate @Inject constructor( ) : CodeScanOperations { sealed interface Event { - data class BillReady(val bill: Bill) : Event + data class BillReady(val bill: Scannable.Payable) : Event data object RefreshFeed : Event data object CheckPendingFeed : Event } @@ -119,7 +119,7 @@ class CodeScanDelegate @Inject constructor( Clock.System.now().toEpochMilliseconds() - it } - val bill = Bill.Cash( + val bill = Scannable.Payable.forToken( amount = amount, token = token, didReceive = true, diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegate.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegate.kt index 4a56d226b..c0a4becff 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegate.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegate.kt @@ -2,7 +2,7 @@ package com.flipcash.app.session.internal.delegates import com.flipcash.app.analytics.Analytics import com.flipcash.app.analytics.FlipcashAnalyticsService -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.BillDeterminationResult import com.flipcash.app.session.Grabbed @@ -80,7 +80,7 @@ class GiftCardSharingDelegate @Inject constructor( sealed interface Event { data class DismissBill(val action: BillDeterminationResult) : Event - data class RestartBillGrab(val bill: Bill, val owner: AccountCluster) : Event + data class RestartBillGrab(val bill: Scannable.Payable, val owner: AccountCluster) : Event data object RefreshFeed : Event } @@ -91,7 +91,7 @@ class GiftCardSharingDelegate @Inject constructor( private val giftCardFundingInProgress = AtomicBoolean(false) - fun shareGiftCard(bill: Bill.Cash, owner: AccountCluster) { + fun shareGiftCard(bill: Scannable.Payable, owner: AccountCluster) { val amount = bill.amount val token = bill.token val verifiedState = bill.verifiedState!! diff --git a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerEventRoutingTest.kt b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerEventRoutingTest.kt index 7c737cacf..1c7d3a665 100644 --- a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerEventRoutingTest.kt +++ b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerEventRoutingTest.kt @@ -2,7 +2,7 @@ package com.flipcash.app.session.internal import com.flipcash.app.appsettings.AppSettingsCoordinator import com.flipcash.app.core.MainCoroutineRule -import com.flipcash.app.core.bill.Bill +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.featureflags.FeatureFlagController import com.flipcash.app.session.BillDeterminationResult @@ -133,7 +133,7 @@ class SessionControllerEventRoutingTest { fun `CashLinkDelegate BillReady event routes to showBill on billDelegate`() = runTest { createController() // init block wires up collectors - val bill = mockk(relaxed = true) + val bill = mockk(relaxed = true) cashLinkDelegateEvents.emit(CashLinkDelegate.Event.BillReady(bill)) advanceUntilIdle() @@ -149,7 +149,7 @@ class SessionControllerEventRoutingTest { fun `CodeScanDelegate BillReady event routes to showBill on billDelegate`() = runTest { createController() - val bill = mockk(relaxed = true) + val bill = mockk(relaxed = true) scanDelegateEvents.emit(CodeScanDelegate.Event.BillReady(bill)) advanceUntilIdle() @@ -165,7 +165,7 @@ class SessionControllerEventRoutingTest { fun `BillPresentationDelegate SendAsLinkRequested event routes to shareGiftCard on giftCardDelegate`() = runTest { createController() - val bill = mockk(relaxed = true) + val bill = mockk(relaxed = true) val owner = mockk(relaxed = true) billDelegateEvents.emit(BillPresentationDelegate.Event.SendAsLinkRequested(bill, owner)) @@ -199,7 +199,7 @@ class SessionControllerEventRoutingTest { fun `GiftCardSharingDelegate RestartBillGrab event routes to awaitBillGrab on billDelegate`() = runTest { createController() - val bill = mockk(relaxed = true) + val bill = mockk(relaxed = true) val owner = mockk(relaxed = true) giftCardDelegateEvents.emit(GiftCardSharingDelegate.Event.RestartBillGrab(bill, owner)) @@ -254,8 +254,8 @@ class SessionControllerEventRoutingTest { fun `multiple BillReady events from different delegates each call showBill once`() = runTest { createController() - val billFromScan = mockk(relaxed = true) - val billFromLink = mockk(relaxed = true) + val billFromScan = mockk(relaxed = true) + val billFromLink = mockk(relaxed = true) scanDelegateEvents.emit(CodeScanDelegate.Event.BillReady(billFromScan)) cashLinkDelegateEvents.emit(CashLinkDelegate.Event.BillReady(billFromLink)) diff --git a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerGiftCardErrorTest.kt b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerGiftCardErrorTest.kt index fd8e18234..d95d161f7 100644 --- a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerGiftCardErrorTest.kt +++ b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/SessionControllerGiftCardErrorTest.kt @@ -2,8 +2,8 @@ package com.flipcash.app.session.internal import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.MainCoroutineRule -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.internal.delegates.BillPresentationDelegate import com.flipcash.app.session.internal.delegates.CashLinkDelegate @@ -224,11 +224,11 @@ class SessionControllerGiftCardErrorTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 1.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) controller.showBill(bill) @@ -266,11 +266,11 @@ class SessionControllerGiftCardErrorTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, verifiedState = mockk(relaxed = true), ) diff --git a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegateTest.kt b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegateTest.kt index 7d5fefe43..59dd38a57 100644 --- a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegateTest.kt +++ b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/BillPresentationDelegateTest.kt @@ -2,8 +2,8 @@ package com.flipcash.app.session.internal.delegates import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.MainCoroutineRule -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.PutInWallet import com.flipcash.app.session.internal.SessionStateHolder @@ -89,11 +89,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 0.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -109,11 +109,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -129,11 +129,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -152,11 +152,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.airdrop, + kind = Scannable.Payable.Kind.airdrop, ) val delegate = createDelegate() @@ -165,6 +165,7 @@ class BillPresentationDelegateTest { val updatedState = updateSlot.captured(BillState.Default) assertNull(updatedState.primaryAction) assertNull(updatedState.secondaryAction) + verify(exactly = 0) { billController.awaitGrab(any(), any(), any(), any(), any(), any(), any(), any(), any()) } } @Test @@ -175,11 +176,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = true, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -198,11 +199,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -220,11 +221,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) val delegate = createDelegate() @@ -283,11 +284,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 3.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) delegate.awaitBillGrab(bill, accountCluster) @@ -324,11 +325,11 @@ class BillPresentationDelegateTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 3.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) delegate.awaitBillGrab(bill, accountCluster) diff --git a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/DelegateEventEmissionTest.kt b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/DelegateEventEmissionTest.kt index 7ca6454f8..aed7df808 100644 --- a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/DelegateEventEmissionTest.kt +++ b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/DelegateEventEmissionTest.kt @@ -3,8 +3,8 @@ package com.flipcash.app.session.internal.delegates import app.cash.turbine.test import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.MainCoroutineRule -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.internal.SessionStateHolder import com.flipcash.app.tokens.TokenCoordinator @@ -87,11 +87,11 @@ class DelegateEventEmissionTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 5.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, verifiedState = mockk(relaxed = true), ) @@ -144,11 +144,11 @@ class DelegateEventEmissionTest { val amount = mockk(relaxed = true) { every { nativeAmount.decimalValue } returns 3.0 } - val bill = Bill.Cash( + val bill = Scannable.CashBill( token = mockk(relaxed = true), amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, ) delegate.awaitBillGrab(bill, accountCluster) diff --git a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegateTest.kt b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegateTest.kt index 01f7fe0be..d8c8bd23d 100644 --- a/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegateTest.kt +++ b/apps/flipcash/shared/session/src/test/kotlin/com/flipcash/app/session/internal/delegates/GiftCardSharingDelegateTest.kt @@ -3,8 +3,8 @@ package com.flipcash.app.session.internal.delegates import app.cash.turbine.test import com.flipcash.app.analytics.FlipcashAnalyticsService import com.flipcash.app.core.MainCoroutineRule -import com.flipcash.app.core.bill.Bill import com.flipcash.app.core.bill.BillState +import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.internal.bill.BillController import com.flipcash.app.session.PutInWallet import com.flipcash.core.R @@ -111,11 +111,11 @@ class GiftCardSharingDelegateTest { ) } - private fun makeBill(): Bill.Cash = Bill.Cash( + private fun makeBill(): Scannable.CashBill = Scannable.CashBill( token = token, amount = amount, didReceive = false, - kind = Bill.Kind.cash, + kind = Scannable.Payable.Kind.cash, verifiedState = verifiedState, ) diff --git a/docs/architecture/06-payments-and-operations.md b/docs/architecture/06-payments-and-operations.md index 7e66b1514..84355bf78 100644 --- a/docs/architecture/06-payments-and-operations.md +++ b/docs/architecture/06-payments-and-operations.md @@ -81,10 +81,9 @@ is `Mint.usdf`: a buy spends USDF to mint tokens (adding USDF to the pool's Creating a currency (`CurrencyCreatorViewModel`) seeds an **initial USDF buy** (default ~$5, from a user flag) and hands the creator a cash bill of the new token. -**Sending/sharing carries any token.** A cash bill (`Bill.Cash`) holds whichever +**Sending/sharing carries any token.** A cash bill (`Scannable.Payable`) holds whichever `Token` is selected — a launchpad currency *or* USDF. Launchpad currencies render as -a custom bill (`renderAsBill = token.address != Mint.usdf`); USDF renders as plain -cash. +`Scannable.CashBill`; USDF renders as `Scannable.GoldBar` (decided at construction time by `Scannable.Payable.forToken`). > **Two senses of "reserves" — don't conflate them.** (1) The on-chain USDF locked in > a launchpad token's `coreMintVault` is the token's **backing**. (2)