@@ -27,8 +27,8 @@ import {
2727 Database ,
2828 CheckCircle ,
2929} from "lucide-react"
30- import { useAccount , useWriteContract , useReadContract , useWaitForTransactionReceipt } from "wagmi"
31- import { parseUnits , formatUnits , erc20Abi } from "viem"
30+ import { useAccount , useWriteContract , useReadContract , useWaitForTransactionReceipt , usePublicClient } from "wagmi"
31+ import { parseUnits , formatUnits , erc20Abi , BaseError } from "viem"
3232
3333function isHexAddress ( value : string | null ) : value is `0x${string } ` {
3434 return ! ! value && / ^ 0 x [ a - f A - F 0 - 9 ] { 40 } $ / . test ( value )
@@ -80,7 +80,6 @@ export default function OracleInteractionPage() {
8080 const [ tokenAllowance , setTokenAllowance ] = useState < string > ( "0" )
8181
8282 const [ priceHistoryPoints , setPriceHistoryPoints ] = useState < PriceChartPoint [ ] > ( [ ] )
83- const [ pendingRead , setPendingRead ] = useState < "aggregated" | "latest" | null > ( null )
8483
8584 // Oracle configuration display values
8685 const [ rewardRate , setRewardRate ] = useState < string > ( "Loading..." )
@@ -137,6 +136,7 @@ export default function OracleInteractionPage() {
137136 const oracleAddress = isHexAddress ( oracleParam ) ? ( oracleParam as `0x${string } `) : null
138137 const chainId = chainIdParam ? Number ( chainIdParam ) : undefined
139138 const chainIdValid = chainId !== undefined && Number . isFinite ( chainId ) && chainId > 0
139+ const publicClient = usePublicClient ( { chainId } )
140140
141141 // Contract write hook
142142 const { writeContract, writeContractAsync, data : hash , error : contractError , isPending } = useWriteContract ( )
@@ -355,6 +355,16 @@ export default function OracleInteractionPage() {
355355 [ userTokenBalance ]
356356 )
357357
358+ const lockedTokensRaw = lockedTokensData ? BigInt ( lockedTokensData as bigint ) : BigInt ( 0 )
359+ const unlockedTokensRaw = unlockedTokensData ? BigInt ( unlockedTokensData as bigint ) : BigInt ( 0 )
360+ const totalDepositedTokensRaw = lockedTokensRaw + unlockedTokensRaw
361+ const userTokenBalanceRaw = userTokenBalanceData ? BigInt ( userTokenBalanceData as bigint ) : BigInt ( 0 )
362+
363+ const isTokenHolder = userTokenBalanceRaw > BigInt ( 0 )
364+ const isActiveOperator = totalDepositedTokensRaw > BigInt ( 0 )
365+ const hasUnlockedTokens = unlockedTokensRaw > BigInt ( 0 )
366+ const operatorActionsDisabled = isActiveOperator && ! hasUnlockedTokens
367+
358368 // Update real-time data when contract data changes
359369 useEffect ( ( ) => {
360370 // Calculate total deposited tokens (sum of locked and unlocked tokens)
@@ -476,31 +486,12 @@ export default function OracleInteractionPage() {
476486 const refreshValues = async ( ) => {
477487 try {
478488 const result = await refetchPriceHistory ( )
479- if ( pendingRead && result ?. data ) {
489+ if ( result ?. data ) {
480490 const data = result . data as PriceHistoryResult
481491 updatePriceDisplays ( data )
482-
483- const points = buildPriceHistoryPoints ( data )
484- if ( points . length > 0 ) {
485- const latestPoint = points [ points . length - 1 ]
486- if ( pendingRead === "aggregated" ) {
487- toast ( {
488- title : "Aggregated Value" ,
489- description : `Current aggregated value: ${ latestPoint . aggregated . toFixed ( DISPLAY_PRECISION ) } ` ,
490- } )
491- }
492- if ( pendingRead === "latest" ) {
493- toast ( {
494- title : "Latest Submission" ,
495- description : `Latest submitted value: ${ latestPoint . latest . toFixed ( DISPLAY_PRECISION ) } ` ,
496- } )
497- }
498- }
499492 }
500493 } catch ( err ) {
501494 console . error ( 'Error updating price data after confirmation:' , err )
502- } finally {
503- setPendingRead ( null )
504495 }
505496 }
506497
@@ -520,7 +511,7 @@ export default function OracleInteractionPage() {
520511 setDepositAmount ( "" )
521512 setWithdrawAmount ( "" )
522513 setVoteTarget ( "" )
523- } , [ isConfirmed , pendingRead , refetchPriceHistory , updatePriceDisplays , buildPriceHistoryPoints , toast ] )
514+ } , [ isConfirmed , refetchPriceHistory , updatePriceDisplays , toast ] )
524515
525516 // Handle transaction errors
526517 useEffect ( ( ) => {
@@ -893,77 +884,118 @@ export default function OracleInteractionPage() {
893884 }
894885 }
895886
896- const handleReadValue = async ( ) => {
897- if ( ! isConnected || ! userAddress ) {
898- toast ( {
899- title : "Wallet Not Connected" ,
900- description : "Please connect your wallet to read values." ,
901- variant : "destructive" ,
902- } )
903- return
904- }
887+ const simulateRead = useCallback (
888+ async ( fnName : "readValue" | "readLatestValue" ) => {
889+ if ( ! oracleAddress || ! publicClient ) {
890+ throw new Error ( "Oracle client not ready" )
891+ }
892+
893+ const attempt = async ( account ?: `0x${string } `) => {
894+ const { result } = await publicClient . simulateContract ( {
895+ address : oracleAddress ,
896+ abi : OracleAbi ,
897+ functionName : fnName ,
898+ args : [ ] ,
899+ account,
900+ } )
901+ return result as bigint
902+ }
903+
904+ let lastError : unknown
905+
906+ if ( userAddress ) {
907+ try {
908+ const result = await attempt ( userAddress as `0x${string } `)
909+ return { result, usedFallback : false }
910+ } catch ( err ) {
911+ lastError = err
912+ }
913+ }
914+
915+ try {
916+ const result = await attempt ( undefined )
917+ if ( userAddress ) {
918+ console . warn ( `[OracleInteraction] Falling back to neutral account for ${ fnName } due to address restrictions.` )
919+ }
920+ return { result, usedFallback : ! ! userAddress }
921+ } catch ( err ) {
922+ if ( err instanceof Error ) {
923+ throw err
924+ }
925+ if ( lastError instanceof Error ) {
926+ throw lastError
927+ }
928+ throw new Error ( "Failed to simulate read call" )
929+ }
930+ } ,
931+ [ oracleAddress , publicClient , userAddress ]
932+ )
905933
934+ const handleReadValue = async ( ) => {
906935 try {
907936 setIsReadingValue ( true )
908- setPendingRead ( "aggregated" )
909937
910- await writeContractAsync ( {
911- address : oracleAddress ! ,
912- abi : OracleAbi ,
913- functionName : 'readValue' ,
914- args : [ ] ,
915- } )
938+ const { result, usedFallback } = await simulateRead ( "readValue" )
939+ const formatted = formatPriceFromWei ( result )
940+
941+ setAggregatedValue ( formatted )
942+ setLastUpdated ( "Just now" )
916943
917944 toast ( {
918- title : "Transaction Submitted" ,
919- description : "Awaiting confirmation to retrieve the aggregated value." ,
945+ title : "Aggregated Value" ,
946+ description : usedFallback
947+ ? `Current aggregated value: ${ formatted } . Retrieved via neutral simulation.`
948+ : `Current aggregated value: ${ formatted } ` ,
920949 } )
921- } catch ( err : any ) {
922- console . error ( 'Error reading value:' , err )
950+ } catch ( err : unknown ) {
951+ console . error ( 'Error reading aggregated value:' , err )
952+ const description =
953+ err instanceof BaseError
954+ ? err . shortMessage
955+ : err instanceof Error
956+ ? err . message
957+ : "Failed to read aggregated value. Please try again."
958+
923959 toast ( {
924960 title : "Read Failed" ,
925- description : err ?. message || "Failed to read aggregated value. Please try again." ,
961+ description,
926962 variant : "destructive" ,
927963 } )
928- setPendingRead ( null )
929964 } finally {
930965 setIsReadingValue ( false )
931966 }
932967 }
933968
934969 const handleReadLatestValue = async ( ) => {
935- if ( ! isConnected || ! userAddress ) {
936- toast ( {
937- title : "Wallet Not Connected" ,
938- description : "Please connect your wallet to read values." ,
939- variant : "destructive" ,
940- } )
941- return
942- }
943-
944970 try {
945971 setIsReadingLatestValue ( true )
946- setPendingRead ( "latest" )
947972
948- await writeContractAsync ( {
949- address : oracleAddress ! ,
950- abi : OracleAbi ,
951- functionName : 'readLatestValue' ,
952- args : [ ] ,
953- } )
973+ const { result, usedFallback } = await simulateRead ( "readLatestValue" )
974+ const formatted = formatPriceFromWei ( result )
975+
976+ setLatestValue ( formatted )
977+ setLastUpdated ( "Just now" )
954978
955979 toast ( {
956- title : "Transaction Submitted" ,
957- description : "Awaiting confirmation to retrieve the latest value." ,
980+ title : "Latest Submission" ,
981+ description : usedFallback
982+ ? `Latest submitted value: ${ formatted } . Retrieved via neutral simulation.`
983+ : `Latest submitted value: ${ formatted } ` ,
958984 } )
959- } catch ( err : any ) {
985+ } catch ( err : unknown ) {
960986 console . error ( 'Error reading latest value:' , err )
987+ const description =
988+ err instanceof BaseError
989+ ? err . shortMessage
990+ : err instanceof Error
991+ ? err . message
992+ : "Failed to read latest value. Please try again."
993+
961994 toast ( {
962995 title : "Read Failed" ,
963- description : err ?. message || "Failed to read latest value. Please try again." ,
996+ description,
964997 variant : "destructive" ,
965998 } )
966- setPendingRead ( null )
967999 } finally {
9681000 setIsReadingLatestValue ( false )
9691001 }
@@ -1088,7 +1120,7 @@ export default function OracleInteractionPage() {
10881120 </ CardTitle >
10891121 </ CardHeader >
10901122 < CardContent className = "p-6" >
1091- < PriceChart data = { priceHistoryPoints } loading = { pendingRead !== null || isFetchingPriceHistory } />
1123+ < PriceChart data = { priceHistoryPoints } loading = { isFetchingPriceHistory || isReadingValue || isReadingLatestValue } />
10921124 </ CardContent >
10931125 </ Card >
10941126
0 commit comments