Skip to content

Commit e8b4e40

Browse files
committed
feat: add metrics around purchases and sells of currency creator tokens
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent d565500 commit e8b4e40

12 files changed

Lines changed: 188 additions & 6 deletions

File tree

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ internal fun AppScreenContent(content: @Composable () -> Unit) {
8181
}
8282

8383
register<AppRoute.Token.Info> {
84-
TokenInfoScreen(it.mint, it.forNeededFunds)
84+
TokenInfoScreen(it.mint, it.forNeededFunds, it.fromDeeplink)
8585
}
8686

8787
register<AppRoute.Token.Transactions> {

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ sealed interface AppRoute : ScreenProvider, Parcelable {
6565

6666
@Parcelize
6767
sealed interface Token: AppRoute {
68-
data class Info(val mint: Mint, val forNeededFunds: Boolean = false): Token
68+
data class Info(val mint: Mint, val forNeededFunds: Boolean = false, val fromDeeplink: Boolean = false): Token
6969
data class Transactions(val mint: Mint): Token
7070
data class SwapTransact(val purpose: TokenSwapPurpose, val forNeededFunds: Boolean = false): Token
7171
data class TxProcessing(val swapId: SwapId): Token

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/NavigationStateRestorer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class NavigationStateRestorer(
2121
navigator.show(
2222
listOf(
2323
ScreenRegistry.get(AppRoute.Sheets.Wallet),
24-
ScreenRegistry.get(AppRoute.Token.Info(deeplink.mint))
24+
ScreenRegistry.get(AppRoute.Token.Info(deeplink.mint, fromDeeplink = true))
2525
)
2626
)
2727
}

apps/flipcash/features/tokens/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies {
5656
implementation(Libs.haze)
5757
implementation(Libs.haze_materials)
5858

59+
implementation(project(":apps:flipcash:shared:analytics"))
5960
implementation(project(":apps:flipcash:shared:onramp:deeplinks"))
6061
implementation(project(":apps:flipcash:shared:shareable"))
6162
implementation(project(":apps:flipcash:shared:tokens"))

apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/TokenInfoScreen.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@ import androidx.compose.ui.Alignment
1010
import androidx.compose.ui.Modifier
1111
import androidx.compose.ui.res.stringResource
1212
import androidx.lifecycle.compose.collectAsStateWithLifecycle
13+
import cafe.adriel.voyager.core.annotation.ExperimentalVoyagerApi
14+
import cafe.adriel.voyager.core.lifecycle.LifecycleEffectOnce
1315
import cafe.adriel.voyager.core.registry.ScreenRegistry
1416
import cafe.adriel.voyager.core.screen.ScreenKey
1517
import cafe.adriel.voyager.core.screen.uniqueScreenKey
1618
import cafe.adriel.voyager.hilt.getViewModel
19+
import com.flipcash.app.analytics.AnalyticsEvent
20+
import com.flipcash.app.analytics.FlipcashAnalyticsService
1721
import com.flipcash.app.core.ui.TokenIconWithName
1822
import com.flipcash.app.onramp.LocalExternalWalletState
1923
import com.flipcash.app.onramp.OnRampFlowTracker
2024
import com.flipcash.app.tokens.internal.TokenInfoScreen
2125
import com.flipcash.app.tokens.ui.TokenInfoViewModel
2226
import com.flipcash.features.tokens.R
2327
import com.flipcash.services.internal.model.thirdparty.OnRampProvider
28+
import com.getcode.libs.analytics.LocalAnalytics
2429
import com.getcode.navigation.core.LocalCodeNavigator
2530
import com.getcode.navigation.modal.ModalScreen
2631
import com.getcode.navigation.screens.AppScreen
@@ -42,11 +47,13 @@ import kotlinx.parcelize.Parcelize
4247
class TokenInfoScreen(
4348
private val mint: Mint,
4449
private val forNeededFunds: Boolean,
50+
private val fromDeeplink: Boolean,
4551
) : AppScreen(), ModalScreen, Parcelable {
4652

4753
@IgnoredOnParcel
4854
override val key: ScreenKey = uniqueScreenKey
4955

56+
@OptIn(ExperimentalVoyagerApi::class)
5057
@Composable
5158
override fun ModalContent() {
5259
val navigator = LocalCodeNavigator.current
@@ -56,6 +63,7 @@ class TokenInfoScreen(
5663
modifier = Modifier.fillMaxSize(),
5764
horizontalAlignment = Alignment.CenterHorizontally,
5865
) {
66+
val analytics = LocalAnalytics.current as FlipcashAnalyticsService
5967
val viewModel = getViewModel<TokenInfoViewModel>()
6068
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
6169
AppBarWithTitle(
@@ -88,6 +96,19 @@ class TokenInfoScreen(
8896
},
8997
)
9098

99+
LifecycleEffectOnce {
100+
val event = when {
101+
forNeededFunds -> AnalyticsEvent.OpenTokenInfoEvent.Give
102+
fromDeeplink -> AnalyticsEvent.OpenTokenInfoEvent.Deeplink
103+
else -> AnalyticsEvent.OpenTokenInfoEvent.Wallet
104+
}
105+
106+
analytics.openTokenInfo(
107+
from = event,
108+
mint = mint
109+
)
110+
}
111+
91112
TokenInfoScreen(viewModel, forNeededFunds)
92113

93114
LaunchedEffect(Unit) {

apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/internal/TokenInfoScreen.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import androidx.compose.ui.res.painterResource
2929
import androidx.compose.ui.res.stringResource
3030
import androidx.compose.ui.unit.dp
3131
import androidx.lifecycle.compose.collectAsStateWithLifecycle
32+
import com.flipcash.app.analytics.Action
33+
import com.flipcash.app.analytics.FlipcashAnalyticsService
3234
import com.flipcash.app.core.AppRoute
3335
import com.flipcash.app.core.data.Loadable
3436
import com.flipcash.app.core.money.RegionSelectionKind
@@ -38,6 +40,7 @@ import com.flipcash.app.tokens.internal.components.info.MarketCapSection
3840
import com.flipcash.app.tokens.internal.components.info.TokenBalance
3941
import com.flipcash.app.tokens.internal.components.info.TokenDetailsSection
4042
import com.flipcash.features.tokens.R
43+
import com.getcode.libs.analytics.LocalAnalytics
4144
import com.getcode.theme.CodeTheme
4245
import com.getcode.ui.core.drawWithGradient
4346
import com.getcode.ui.core.measured
@@ -264,6 +267,7 @@ private fun BottomBarButtons(
264267
modifier: Modifier = Modifier,
265268
dispatch: (TokenInfoViewModel.Event) -> Unit
266269
) {
270+
val analytics = LocalAnalytics.current as FlipcashAnalyticsService
267271
when (val loadable = state.token) {
268272
is Loadable.Error -> Unit
269273
is Loadable.Loaded -> {
@@ -290,6 +294,7 @@ private fun BottomBarButtons(
290294
buttonState = ButtonState.Filled20,
291295
text = stringResource(R.string.action_sell),
292296
) {
297+
analytics.action(Action.Sell)
293298
dispatch(
294299
TokenInfoViewModel.Event.OpenScreen(
295300
AppRoute.Token.SwapTransact(

apps/flipcash/shared/analytics/src/main/kotlin/com/flipcash/app/analytics/Actions.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,16 @@ sealed class Action : AppAction {
3030
data object CompletedOnboarding : Action() {
3131
override val value: String = "Complete Onboarding"
3232
}
33+
34+
data object BuyWithReserves : Action() {
35+
override val value: String = "Button: Buy With Reserves"
36+
}
37+
38+
data object BuyWithPhantom : Action() {
39+
override val value: String = "Button: Buy With Phantom"
40+
}
41+
42+
data object Sell : Action() {
43+
override val value: String = "Button: Sell"
44+
}
3345
}

apps/flipcash/shared/analytics/src/main/kotlin/com/flipcash/app/analytics/Analytics.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import com.getcode.libs.analytics.AppActionSource
88
import com.getcode.opencode.model.financial.CurrencyCode
99
import com.getcode.opencode.model.financial.Fiat
1010
import com.getcode.opencode.model.financial.LocalFiat
11+
import com.getcode.solana.keys.Mint
1112

12-
interface FlipcashAnalyticsService: AnalyticsService {
13+
interface FlipcashAnalyticsService : AnalyticsService {
1314
fun transfer(
1415
event: AnalyticsEvent.Transfer,
1516
amount: LocalFiat? = null,
@@ -59,6 +60,21 @@ interface FlipcashAnalyticsService: AnalyticsService {
5960
fun transactionSubmittedToWallet(provider: OnRampProvider.UsesDeeplinks)
6061
fun walletTransactionFailed(provider: OnRampProvider.UsesDeeplinks)
6162
fun walletTransactionCancelled(provider: OnRampProvider.UsesDeeplinks)
63+
64+
fun openTokenInfo(from: AnalyticsEvent.OpenTokenInfoEvent, mint: Mint)
65+
fun buy(
66+
method: AnalyticsEvent.TokenTransactionEvent.Purchase,
67+
amount: Fiat,
68+
mint: Mint,
69+
error: Throwable? = null
70+
)
71+
72+
fun sell(
73+
amount: Fiat,
74+
feeAmount: Fiat,
75+
mint: Mint,
76+
error: Throwable? = null
77+
)
6278
}
6379

6480
class StubFlipcashAnalytics : FlipcashAnalyticsService {
@@ -103,4 +119,14 @@ class StubFlipcashAnalytics : FlipcashAnalyticsService {
103119
override fun transactionSubmittedToWallet(provider: OnRampProvider.UsesDeeplinks) = Unit
104120
override fun walletTransactionFailed(provider: OnRampProvider.UsesDeeplinks) = Unit
105121
override fun walletTransactionCancelled(provider: OnRampProvider.UsesDeeplinks) = Unit
122+
123+
override fun openTokenInfo(from: AnalyticsEvent.OpenTokenInfoEvent, mint: Mint) = Unit
124+
override fun buy(
125+
method: AnalyticsEvent.TokenTransactionEvent.Purchase,
126+
amount: Fiat,
127+
mint: Mint,
128+
error: Throwable?
129+
) = Unit
130+
131+
override fun sell(amount: Fiat, feeAmount: Fiat, mint: Mint, error: Throwable?) = Unit
106132
}

apps/flipcash/shared/analytics/src/main/kotlin/com/flipcash/app/analytics/Events.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,40 @@ sealed interface AnalyticsEvent {
119119
}
120120
}
121121

122+
sealed interface OpenTokenInfoEvent : AnalyticsEvent {
123+
data object Deeplink : OpenTokenInfoEvent {
124+
override val name: String = "Token Info: Opened From Deeplink"
125+
}
126+
127+
data object Wallet : OpenTokenInfoEvent {
128+
override val name: String = "Token Info: Opened From Wallet"
129+
}
130+
131+
data object Give : OpenTokenInfoEvent {
132+
override val name: String = "Token Info: Opened From Give"
133+
}
134+
}
135+
136+
sealed interface TokenTransactionEvent: AnalyticsEvent {
137+
sealed interface Purchase: TokenTransactionEvent {
138+
data object Reserves: Purchase {
139+
override val name: String = "Token Purchase With Reserves"
140+
}
141+
142+
data object Phantom: Purchase {
143+
override val name: String = "Token Purchase With Phantom"
144+
}
145+
146+
data object Coinbase: Purchase {
147+
override val name: String = "Token Purchase With Coinbase"
148+
}
149+
}
150+
151+
data object Sell: TokenTransactionEvent {
152+
override val name: String = "Token Sell"
153+
}
154+
}
155+
122156
sealed interface WalletEvent : AnalyticsEvent {
123157
val provider: OnRampProvider.UsesDeeplinks
124158
}

apps/flipcash/shared/analytics/src/main/kotlin/com/flipcash/app/analytics/internal/MixpanelAnalyticsDelegate.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.getcode.opencode.model.financial.CurrencyCode
1010
import com.getcode.opencode.model.financial.Fiat
1111
import com.getcode.opencode.model.financial.LocalFiat
1212
import com.getcode.services.flipcash.BuildConfig
13+
import com.getcode.solana.keys.Mint
1314
import com.getcode.solana.keys.base58
1415
import com.getcode.utils.TraceType
1516
import com.getcode.utils.base58
@@ -142,10 +143,40 @@ internal class MixpanelAnalyticsDelegate @Inject constructor(
142143
track(event.name, *properties.toList().toTypedArray())
143144
}
144145

146+
override fun openTokenInfo(from: AnalyticsEvent.OpenTokenInfoEvent, mint: Mint) {
147+
val properties = from.properties(mint = mint)
148+
track(from.name, *properties.toList().toTypedArray())
149+
}
150+
151+
override fun buy(
152+
method: AnalyticsEvent.TokenTransactionEvent.Purchase,
153+
amount: Fiat,
154+
mint: Mint,
155+
error: Throwable?
156+
) {
157+
val properties = method.properties(mint = mint, nativeAmount = amount, error = error)
158+
track(method.name, *properties.toList().toTypedArray())
159+
}
160+
161+
override fun sell(
162+
amount: Fiat,
163+
feeAmount: Fiat,
164+
mint: Mint,
165+
error: Throwable?
166+
) {
167+
val event = AnalyticsEvent.TokenTransactionEvent.Sell
168+
val properties = event.properties(mint = mint, nativeAmount = amount, error = error)
169+
track(event.name, *properties.toList().toTypedArray())
170+
}
171+
145172
private fun track(name: String, vararg properties: Pair<String, String>) {
146173
if (BuildConfig.DEBUG) {
174+
val propsString = properties.joinToString { "${it.first} => ${it.second}" }
147175
trace(
148-
"debug track ${name}, ${properties.joinToString { "${it.first} => ${it.second}" }}",
176+
buildString {
177+
append("debug track $name")
178+
if (propsString.isNotEmpty()) append(", $propsString")
179+
},
149180
type = TraceType.Silent
150181
)
151182
return
@@ -162,6 +193,8 @@ internal class MixpanelAnalyticsDelegate @Inject constructor(
162193
private fun AnalyticsEvent.properties(
163194
localizedAmount: LocalFiat? = null,
164195
nativeAmount: Fiat? = null,
196+
feeAmount: Fiat? = null,
197+
mint: Mint? = null,
165198
grabTime: Long? = null,
166199
successful: Boolean? = null,
167200
error: Throwable? = null,
@@ -233,6 +266,17 @@ private fun AnalyticsEvent.properties(
233266
}
234267

235268
is AnalyticsEvent.OnRampOpenEvent -> Unit
269+
is AnalyticsEvent.OpenTokenInfoEvent -> {
270+
put("Mint", mint?.base58().orEmpty())
271+
}
272+
is AnalyticsEvent.TokenTransactionEvent -> {
273+
put("Mint", mint?.base58().orEmpty())
274+
put("Fiat", nativeAmount?.decimalValue.toString())
275+
if (feeAmount != null) {
276+
put("Fee", feeAmount.decimalValue.toString())
277+
}
278+
put("Currency", nativeAmount?.currencyCode?.name.orEmpty())
279+
}
236280
is AnalyticsEvent.OnRampVerificationEvent -> Unit
237281
AnalyticsEvent.OnRampPurchaseEvent.Completed -> {
238282
put("Fiat", nativeAmount?.decimalValue.toString())

0 commit comments

Comments
 (0)