@@ -30,7 +30,9 @@ import {
3030import { useWalletBalance } from "../../../../core/hooks/others/useWalletBalance.js" ;
3131import { ConnectButton } from "../../ConnectWallet/ConnectButton.js" ;
3232import { ArrowUpDownIcon } from "../../ConnectWallet/icons/ArrowUpDownIcon.js" ;
33+ import { WalletDotIcon } from "../../ConnectWallet/icons/WalletDotIcon.js" ;
3334import { PoweredByThirdweb } from "../../ConnectWallet/PoweredByTW.js" ;
35+ import { formatTokenAmount } from "../../ConnectWallet/screens/formatTokenBalance.js" ;
3436import { Container } from "../../components/basic.js" ;
3537import { Button } from "../../components/buttons.js" ;
3638import { Input } from "../../components/formElements.js" ;
@@ -351,10 +353,34 @@ function SwapUIBase(
351353 walletAddress : props . activeWalletInfo ?. activeAccount . address ,
352354 } ) ;
353355
356+ const buyTokenBalanceQuery = useTokenBalance ( {
357+ chainId : buyTokenWithPrices ?. chainId ,
358+ tokenAddress : buyTokenWithPrices ?. address ,
359+ client : props . client ,
360+ walletAddress : props . activeWalletInfo ?. activeAccount . address ,
361+ } ) ;
362+
363+ const notEnoughBalance = ! ! (
364+ props . amountSelection . type === "sell" &&
365+ sellTokenBalanceQuery . data ?. value &&
366+ sellTokenWithPrices ?. decimals &&
367+ props . amountSelection . amount &&
368+ sellTokenBalanceQuery . data . value <
369+ Number (
370+ toUnits ( props . amountSelection . amount , sellTokenWithPrices . decimals ) ,
371+ )
372+ ) ;
373+
354374 return (
355375 < Container p = "md" >
356376 { /* Sell */ }
357377 < TokenSection
378+ isConnected = { ! ! props . activeWalletInfo }
379+ notEnoughBalance = { notEnoughBalance }
380+ balance = { {
381+ data : sellTokenBalanceQuery . data ?. value ,
382+ isFetching : sellTokenBalanceQuery . isFetching ,
383+ } }
358384 amount = { {
359385 data : sellTokenAmount ,
360386 isFetching : isSellAmountFetching ,
@@ -388,6 +414,12 @@ function SwapUIBase(
388414
389415 { /* Buy */ }
390416 < TokenSection
417+ isConnected = { ! ! props . activeWalletInfo }
418+ notEnoughBalance = { false }
419+ balance = { {
420+ data : buyTokenBalanceQuery . data ?. value ,
421+ isFetching : buyTokenBalanceQuery . isFetching ,
422+ } }
391423 amount = { {
392424 data : buyTokenAmount ,
393425 isFetching : isBuyAmountFetching ,
@@ -427,7 +459,11 @@ function SwapUIBase(
427459 />
428460 ) : (
429461 < Button
430- disabled = { ! preparedResultQuery . data || preparedResultQuery . isFetching }
462+ disabled = {
463+ ! preparedResultQuery . data ||
464+ preparedResultQuery . isFetching ||
465+ notEnoughBalance
466+ }
431467 fullWidth
432468 onClick = { ( ) => {
433469 if (
@@ -524,6 +560,7 @@ function DecimalInput(props: {
524560
525561function TokenSection ( props : {
526562 label : string ;
563+ notEnoughBalance : boolean ;
527564 amount : {
528565 data : string ;
529566 isFetching : boolean ;
@@ -538,6 +575,11 @@ function TokenSection(props: {
538575 currency : SupportedFiatCurrency ;
539576 onSelectToken : ( ) => void ;
540577 client : ThirdwebClient ;
578+ isConnected : boolean ;
579+ balance : {
580+ data : bigint | undefined ;
581+ isFetching : boolean ;
582+ } ;
541583} ) {
542584 const chainQuery = useBridgeChains ( props . client ) ;
543585 const chain = chainQuery . data ?. find (
@@ -567,9 +609,10 @@ function TokenSection(props: {
567609 style = { {
568610 display : "flex" ,
569611 alignItems : "center" ,
570- minHeight : "60px" ,
571612 justifyContent : "space-between" ,
572613 gap : spacing . sm ,
614+ paddingBottom : spacing . sm ,
615+ paddingTop : spacing . xs ,
573616 } }
574617 >
575618 { props . amount . isFetching ? (
@@ -603,27 +646,90 @@ function TokenSection(props: {
603646 ) }
604647 </ div >
605648
606- { /* Fiat Value */ }
607- < div style = { { display : "flex" , alignItems : "center" , gap : "4px" } } >
608- < Text size = "md" color = "secondaryText" weight = { 500 } >
609- { getFiatSymbol ( props . currency ) }
610- </ Text >
611- { props . amount . isFetching ? (
612- < Skeleton height = { fontSize . md } width = "50px" />
613- ) : (
614- < Text size = "md" color = "secondaryText" weight = { 500 } >
615- { totalFiatValue === undefined
616- ? "0.00"
617- : totalFiatValue < 0.01
618- ? "~0.00"
619- : totalFiatValue . toFixed ( 2 ) }
649+ < div
650+ style = { {
651+ display : "flex" ,
652+ alignItems : "center" ,
653+ gap : "4px" ,
654+ justifyContent : "space-between" ,
655+ } }
656+ >
657+ { /* Exceeds Balance / Fiat Value */ }
658+ { props . notEnoughBalance ? (
659+ < Text size = "md" color = "danger" weight = { 500 } >
660+ { " " }
661+ Exceeds Balance{ " " }
620662 </ Text >
663+ ) : (
664+ < div
665+ style = { {
666+ display : "flex" ,
667+ alignItems : "center" ,
668+ gap : "4px" ,
669+ } }
670+ >
671+ < Text size = "md" color = "secondaryText" weight = { 500 } >
672+ { getFiatSymbol ( props . currency ) }
673+ </ Text >
674+ { props . amount . isFetching ? (
675+ < Skeleton height = { fontSize . md } width = "50px" />
676+ ) : (
677+ < div >
678+ < DecimalRenderer value = { totalFiatValue ?. toFixed ( 5 ) || "0.00" } />
679+ </ div >
680+ ) }
681+ </ div >
682+ ) }
683+
684+ { /* Balance */ }
685+ { props . isConnected && (
686+ < div >
687+ { props . balance . isFetching ||
688+ props . balance . data === undefined ||
689+ ! props . selectedToken ?. data ? (
690+ < Skeleton height = { fontSize . md } width = "50px" />
691+ ) : (
692+ < Text
693+ size = "md"
694+ color = "secondaryText"
695+ weight = { 500 }
696+ style = { {
697+ display : "flex" ,
698+ alignItems : "center" ,
699+ gap : spacing . xxs ,
700+ } }
701+ >
702+ < WalletDotIcon size = { fontSize . sm } />
703+ < DecimalRenderer
704+ value = { formatTokenAmount (
705+ props . balance . data ,
706+ props . selectedToken . data . decimals ,
707+ 5 ,
708+ ) }
709+ />
710+ </ Text >
711+ ) }
712+ </ div >
621713 ) }
622714 </ div >
623715 </ Container >
624716 ) ;
625717}
626718
719+ function DecimalRenderer ( props : { value : string } ) {
720+ const [ integerPart , fractionPart ] = props . value . split ( "." ) ;
721+ return (
722+ < div style = { { display : "flex" , alignItems : "baseline" } } >
723+ < Text size = "md" color = "secondaryText" weight = { 500 } >
724+ { integerPart } .
725+ </ Text >
726+ < Text size = "sm" color = "secondaryText" weight = { 500 } >
727+ { fractionPart || "00000" }
728+ </ Text >
729+ </ div >
730+ ) ;
731+ }
732+
627733function SelectedTokenButton ( props : {
628734 selectedToken :
629735 | {
0 commit comments