@@ -19,12 +19,12 @@ import { ChainBadge } from '@/components/wallet/chain-icon';
1919import { PatternLock , patternToString } from '@/components/security/pattern-lock' ;
2020import { PasswordInput } from '@/components/security/password-input' ;
2121import { getChainProvider } from '@/services/chain-adapter/providers' ;
22- import { Amount } from '@/types/amount' ;
2322import { signUnsignedTransaction } from '@/services/ecosystem/handlers' ;
2423import { chainConfigService } from '@/services/chain-config/service' ;
2524import { useToast } from '@/services' ;
2625import { findMiniappWalletIdByAddress , resolveMiniappChainId } from './miniapp-wallet' ;
2726import { createMiniappUnsupportedPipelineError , mapMiniappTransferErrorToMessage } from './miniapp-transfer-error' ;
27+ import { parseMiniappTransferAmountRaw } from './miniapp-transfer-amount' ;
2828import {
2929 isMiniappWalletLockError ,
3030 isMiniappTwoStepSecretError ,
@@ -143,10 +143,25 @@ function MiniappTransferConfirmJobContent() {
143143 const chainSymbol = chainConfigService . getSymbol ( resolvedChainId ) ;
144144 return chainSymbol || resolvedChainId . toUpperCase ( ) ;
145145 } , [ asset , resolvedChainId ] ) ;
146+ const displayDecimals = useMemo ( ( ) => chainConfigService . getDecimals ( resolvedChainId ) , [ resolvedChainId ] ) ;
147+
148+ const parsedAmount = useMemo ( ( ) => {
149+ try {
150+ return parseMiniappTransferAmountRaw ( amount , displayDecimals , displayAsset ) ;
151+ } catch {
152+ return null ;
153+ }
154+ } , [ amount , displayDecimals , displayAsset ] ) ;
155+
156+ const displayAmount = useMemo ( ( ) => parsedAmount ?. toFormatted ( { trimTrailingZeros : false } ) ?? amount , [ parsedAmount , amount ] ) ;
157+ const amountInvalidMessage = useMemo (
158+ ( ) => ( parsedAmount ? null : t ( 'transaction:broadcast.invalidParams' ) ) ,
159+ [ parsedAmount , t ] ,
160+ ) ;
146161
147162 const transferShortTitle = useMemo (
148- ( ) => t ( 'transaction:miniappTransfer.shortTitle' , { amount, asset : displayAsset } ) ,
149- [ t , amount , displayAsset ] ,
163+ ( ) => t ( 'transaction:miniappTransfer.shortTitle' , { amount : displayAmount , asset : displayAsset } ) ,
164+ [ t , displayAmount , displayAsset ] ,
150165 ) ;
151166 const isBuilding = phase === 'building' ;
152167 const isBroadcasting = phase === 'broadcasting' ;
@@ -405,16 +420,15 @@ function MiniappTransferConfirmJobContent() {
405420 throw createMiniappUnsupportedPipelineError ( resolvedChainId ) ;
406421 }
407422
408- const decimals = chainConfigService . getDecimals ( resolvedChainId ) ;
409- const chainSymbol = chainConfigService . getSymbol ( resolvedChainId ) ;
410- const symbol = ( asset ?? chainSymbol ) || resolvedChainId . toUpperCase ( ) ;
411- const valueAmount = Amount . parse ( amount , decimals , symbol ) ;
423+ if ( ! parsedAmount ) {
424+ throw new Error ( 'Invalid miniapp transfer amount' ) ;
425+ }
412426
413427 const unsignedTx = await provider . buildTransaction ( {
414428 type : 'transfer' ,
415429 from,
416430 to,
417- amount : valueAmount ,
431+ amount : parsedAmount ,
418432 ...( asset ? { bioAssetType : asset } : { } ) ,
419433 } ) ;
420434
@@ -446,7 +460,7 @@ function MiniappTransferConfirmJobContent() {
446460
447461 return { txHash, transaction } ;
448462 } ,
449- [ resolvedChainId , asset , amount , from , to , walletId ] ,
463+ [ resolvedChainId , parsedAmount , asset , from , to , walletId ] ,
450464 ) ;
451465
452466 const handleTransferFailure = useCallback (
@@ -674,11 +688,11 @@ function MiniappTransferConfirmJobContent() {
674688 < div className = "space-y-4 p-4" >
675689 < div className = "bg-muted/50 rounded-xl p-4 text-center" >
676690 < AmountDisplay
677- value = { amount }
691+ value = { displayAmount }
678692 symbol = { displayAsset }
679693 size = "xl"
680694 weight = "bold"
681- decimals = { 8 }
695+ decimals = { displayDecimals }
682696 fixedDecimals = { true }
683697 />
684698 </ div >
@@ -705,6 +719,12 @@ function MiniappTransferConfirmJobContent() {
705719 </ div >
706720 ) }
707721
722+ { amountInvalidMessage && (
723+ < div className = "bg-destructive/10 text-destructive rounded-xl p-3 text-sm" >
724+ { amountInvalidMessage }
725+ </ div >
726+ ) }
727+
708728 < div className = "bg-muted/50 flex items-center justify-between rounded-xl p-3" >
709729 < span className = "text-muted-foreground text-sm" > { t ( 'network' ) } </ span >
710730 < ChainBadge chainId = { resolvedChainId } />
@@ -727,7 +747,7 @@ function MiniappTransferConfirmJobContent() {
727747 < button
728748 data-testid = "miniapp-transfer-review-confirm"
729749 onClick = { handleEnterWalletLockStep }
730- disabled = { isBusy || ! walletId || isResolvingTwoStepSecret }
750+ disabled = { isBusy || ! walletId || isResolvingTwoStepSecret || ! parsedAmount }
731751 className = { cn (
732752 'flex-1 rounded-xl py-3 font-medium transition-colors' ,
733753 'bg-primary text-primary-foreground hover:bg-primary/90' ,
@@ -847,11 +867,11 @@ function MiniappTransferConfirmJobContent() {
847867 < div className = "space-y-4 p-4" >
848868 < div className = "bg-muted/50 rounded-xl p-4 text-center" >
849869 < AmountDisplay
850- value = { amount }
870+ value = { displayAmount }
851871 symbol = { displayAsset }
852872 size = "xl"
853873 weight = "bold"
854- decimals = { 8 }
874+ decimals = { displayDecimals }
855875 fixedDecimals = { true }
856876 />
857877 </ div >
0 commit comments