@@ -45,6 +45,9 @@ import com.getcode.opencode.model.financial.MintMetadata
4545import com.getcode.opencode.model.financial.Token
4646import com.getcode.opencode.model.financial.TokenCreateRequest
4747import com.getcode.opencode.model.financial.fromLaunch
48+ import com.getcode.opencode.model.financial.minus
49+ import com.getcode.opencode.model.financial.orZero
50+ import com.getcode.opencode.model.financial.plus
4851import com.getcode.opencode.model.moderation.ModerationAttestation
4952import com.getcode.opencode.model.transactions.SwapFundingSource
5053import com.getcode.solana.keys.Mint
@@ -107,7 +110,8 @@ internal class CurrencyCreatorViewModel @Inject constructor(
107110 val bill : Bill ? = null ,
108111 val createdMint : Mint ? = null ,
109112 val launchedToken : Token ? = null ,
110- val purchaseAmount : Fiat = 20 .toFiat(),
113+ val purchaseAmount : Fiat = 5 .toFiat(),
114+ val feeAmount : Fiat ? = null ,
111115 val processingState : LoadingSuccessState = LoadingSuccessState (),
112116 val attestations : ModerationAttestations = ModerationAttestations (),
113117 ) {
@@ -132,6 +136,12 @@ internal class CurrencyCreatorViewModel @Inject constructor(
132136 return (index + 1 ).toFloat() / PROGRESS_STEPS .size
133137 }
134138
139+ val totalCost: Fiat
140+ get() {
141+ val fee = feeAmount.orZero()
142+ return purchaseAmount + fee
143+ }
144+
135145 private companion object {
136146 private const val MAX_DESCRIPTION = 500
137147
@@ -159,7 +169,7 @@ internal class CurrencyCreatorViewModel @Inject constructor(
159169 data class OnIconSelected (val image : Uri ) : Event
160170 data class OnIconCached (val image : Uri ) : Event
161171
162- data class OnPurchaseAmountChanged (val amount : Fiat ) : Event
172+ data class OnPurchaseAmountChanged (val amount : Fiat , val feeAmount : Fiat ) : Event
163173
164174 data class OnBillConfirmed (val bill : Bill ? ) : Event
165175 data class UpdateProcessingState (
@@ -172,24 +182,28 @@ internal class CurrencyCreatorViewModel @Inject constructor(
172182 data class LaunchToken (val method : PurchaseMethod ) : Event
173183 data class OnTokenMinted (val mint : Mint ): Event
174184 data object Purchase : Event
175- data class PurchaseWithReserves (val token : Token , val amount : Fiat ) : Event
176- data class PurchaseWithPhantom (val token : Token , val amount : Fiat ) : Event
177- data class PurchaseWithGooglePay (val token : Token , val amount : Fiat ) : Event
185+ data class PurchaseWithReserves (val context : LaunchedContext ) : Event
186+ data class PurchaseWithPhantom (val context : LaunchedContext ) : Event
187+ data class PurchaseWithGooglePay (val context : LaunchedContext ) : Event
178188
179189 data class PurchaseSubmitted (val swapId : SwapId , val mint : Mint ) : Event
180190 data class PurchaseCompleted (val token : Token ): Event
181191 }
182192
183- private data class LaunchedContext (
193+ data class LaunchedContext (
184194 val method : PurchaseMethod ,
185195 val token : Token ,
186196 val amount : Fiat ,
197+ val feeAmount : Fiat ? ,
187198 )
188199
189200 init {
190201 userFlags.resolvedFlags
191- .map { it.newCurrencyPurchaseAmount.effectiveValue }
192- .onEach { dispatchEvent(Event .OnPurchaseAmountChanged (it)) }
202+ .onEach { flags ->
203+ val purchaseAmount = flags.newCurrencyPurchaseAmount.effectiveValue
204+ val feeAmount = flags.newCurrencyFeeAmount.effectiveValue
205+ dispatchEvent(Event .OnPurchaseAmountChanged (purchaseAmount, feeAmount))
206+ }
193207 .launchIn(viewModelScope)
194208
195209 // Debounced draft persistence — save state 300ms after changes.
@@ -243,11 +257,11 @@ internal class CurrencyCreatorViewModel @Inject constructor(
243257 .filterIsInstance<Event .CheckName >()
244258 .map { stateFlow.value.nameFieldState.text.toString() }
245259 .onEach { dispatchEvent(Event .UpdateProcessingState (loading = true )) }
246- .map { moderationController.moderateText(it) }
260+ .map { moderationController.moderateText(it.trim() ) }
247261 .flatMapResult { result ->
248262 when (result.flaggedCategory) {
249263 ModerationResult .FlaggedCategory .NONE -> {
250- currencyController.checkTokenAvailability(result.text)
264+ currencyController.checkTokenAvailability(result.text.trim() )
251265 .map { result.attestation }
252266 }
253267
@@ -346,7 +360,7 @@ internal class CurrencyCreatorViewModel @Inject constructor(
346360 .filterIsInstance<Event .CheckDescription >()
347361 .map { stateFlow.value.descriptionFieldState.text.toString() }
348362 .onEach { dispatchEvent(Event .UpdateProcessingState (loading = true )) }
349- .map { moderationController.moderateText(it) }
363+ .map { moderationController.moderateText(it.trim() ) }
350364 .flatMapResult { result ->
351365 when (result.flaggedCategory) {
352366 ModerationResult .FlaggedCategory .NONE -> Result .success(result.attestation)
@@ -389,7 +403,8 @@ internal class CurrencyCreatorViewModel @Inject constructor(
389403 .onEach {
390404 val metadata = PurchaseMethodMetadata (
391405 mint = null ,
392- purchaseAmount = stateFlow.value.purchaseAmount,
406+ purchaseAmount = stateFlow.value.totalCost,
407+ feeAmount = stateFlow.value.feeAmount,
393408 paymentAction = PaymentAction .Pay ,
394409 )
395410 purchaseMethodController.present(metadata)
@@ -408,11 +423,11 @@ internal class CurrencyCreatorViewModel @Inject constructor(
408423 println (" customizations=${stateFlow.value.customizations} " )
409424 val request = TokenCreateRequest (
410425 name = ModerationAttestation .Text (
411- text = stateFlow.value.nameFieldState.text.toString(),
426+ text = stateFlow.value.nameFieldState.text.trim(). toString(),
412427 attestation = stateFlow.value.attestations.name.rawValue,
413428 ),
414429 description = ModerationAttestation .Text (
415- text = stateFlow.value.descriptionFieldState.text.toString(),
430+ text = stateFlow.value.descriptionFieldState.text.trim(). toString(),
416431 attestation = stateFlow.value.attestations.description.rawValue
417432 ),
418433 icon = ModerationAttestation .Image (
@@ -452,20 +467,25 @@ internal class CurrencyCreatorViewModel @Inject constructor(
452467 request = request,
453468 owner = accountCluster.authorityPublicKey,
454469 )
455- LaunchedContext (method, token, stateFlow.value.purchaseAmount)
470+ LaunchedContext (
471+ method = method,
472+ token = token,
473+ amount = stateFlow.value.totalCost,
474+ feeAmount = stateFlow.value.feeAmount,
475+ )
456476 }
457477 }
458478 .onResult(
459479 onSuccess = { ctx ->
460480 when (ctx.method) {
461481 is PurchaseMethod .CashReserves ->
462- dispatchEvent(Event .PurchaseWithReserves (ctx.token, ctx.amount ))
482+ dispatchEvent(Event .PurchaseWithReserves (ctx))
463483
464484 PurchaseMethod .PhantomWallet ->
465- dispatchEvent(Event .PurchaseWithPhantom (ctx.token, ctx.amount ))
485+ dispatchEvent(Event .PurchaseWithPhantom (ctx))
466486
467487 PurchaseMethod .CoinbaseOnRamp -> {
468- dispatchEvent(Event .PurchaseWithGooglePay (ctx.token, ctx.amount ))
488+ dispatchEvent(Event .PurchaseWithGooglePay (ctx))
469489 }
470490 }
471491 },
@@ -487,26 +507,30 @@ internal class CurrencyCreatorViewModel @Inject constructor(
487507 AppRoute .Token .CurrencyCreator ,
488508 OnRampProvider .Phantom
489509 )
490- externalWalletController.setAmount(LocalFiat (usdf = event.amount))
491- externalWalletController.setTokenToPurchase(event.token)
510+ val totalAmount = LocalFiat (usdf = event.context.amount)
511+ println (" total amount ${totalAmount.underlyingTokenAmount} " )
512+ val feeAmount = event.context.feeAmount?.let { LocalFiat (usdf = it) }
513+ externalWalletController.setAmount(amount = totalAmount, feeAmount = feeAmount)
514+ externalWalletController.setTokenToPurchase(event.context.token)
492515 }
493516 .launchIn(viewModelScope)
494517
495518 eventFlow
496519 .filterIsInstance<Event .PurchaseWithReserves >()
497520 .mapNotNull { event ->
498521 val owner = userManager.accountCluster ? : return @mapNotNull null
499- Triple (owner, event.token, event.amount )
522+ Pair (owner, event.context )
500523 }
501524 .onEach { dispatchEvent(Event .UpdateProcessingState (loading = true )) }
502- .map { (owner, token, amount ) ->
525+ .map { (owner, context ) ->
503526 transactionController.buy(
504527 owner = owner,
505- amount = LocalFiat (usdf = amount),
506- of = token,
528+ amount = LocalFiat (usdf = context.amount),
529+ feeAmount = context.feeAmount?.let { LocalFiat (usdf = it) },
530+ of = context.token,
507531 source = SwapFundingSource .SubmitIntent (),
508532 fund = null ,
509- ).map { swapId -> swapId to token.address }
533+ ).map { swapId -> swapId to context. token.address }
510534 }
511535 .onResult(
512536 onSuccess = { (swapId, mint) ->
@@ -600,7 +624,7 @@ internal class CurrencyCreatorViewModel @Inject constructor(
600624 }
601625
602626 is Event .OnPurchaseAmountChanged -> { state ->
603- state.copy(purchaseAmount = event.amount)
627+ state.copy(purchaseAmount = event.amount, feeAmount = event.feeAmount )
604628 }
605629
606630 is Event .UpdateProcessingState -> { state ->
0 commit comments