11
22"use client" ;
33
4- import React , { useState , useEffect } from 'react' ;
4+ import React , { useState , useEffect , useMemo } from 'react' ;
55import { config as appConfig } from '../config' ;
6- import { useAccount , useWriteContract , useWaitForTransactionReceipt , useReadContract } from 'wagmi' ;
6+ import { useAccount , useWriteContract , useWaitForTransactionReceipt , useReadContract , useBalance } from 'wagmi' ;
77import { parseEther , formatEther } from 'viem' ;
88
99const registryAddress = appConfig . registryAddress as `0x${string } `;
1010const rawChainId = appConfig . chainId ;
11- const chainId = ( ( ) => {
12- if ( Number . isFinite ( rawChainId ) && rawChainId > 0 ) {
13- return BigInt ( rawChainId ) ;
14- }
15- console . error ( 'Invalid chainId:' , rawChainId , '- using default 421614' ) ;
16- return BigInt ( 421614 ) ;
17- } ) ( ) ;
11+ const chainId = BigInt ( rawChainId || 421614 ) ;
1812
1913const RegistryAbi = [
2014 {
@@ -44,7 +38,7 @@ const RegistryAbi = [
4438] as const ;
4539
4640const OperatorIncentives = ( ) => {
47- const { isConnected } = useAccount ( ) ;
41+ const { address , isConnected } = useAccount ( ) ;
4842 const [ depositAmount , setDepositAmount ] = useState ( '' ) ;
4943 const [ withdrawAmount , setWithdrawAmount ] = useState ( '' ) ;
5044 const [ mounted , setMounted ] = useState ( false ) ;
@@ -54,15 +48,23 @@ const OperatorIncentives = () => {
5448 } , [ ] ) ;
5549
5650 // Read Bond
57- const { data : bondVal , refetch : refetchBond } = useReadContract ( {
51+ const { data : bondVal , refetch : refetchBond , isLoading : isLoadingBond } = useReadContract ( {
5852 address : registryAddress ,
5953 abi : RegistryAbi ,
6054 functionName : 'getBond' ,
6155 args : [ chainId ] ,
56+ query : {
57+ enabled : mounted
58+ }
59+ } ) ;
60+
61+ // Read User Balance for context
62+ const { data : balanceData } = useBalance ( {
63+ address : address ,
6264 } ) ;
6365
6466 // Write Actions
65- const { data : hash , isPending, writeContract, error : writeError } = useWriteContract ( ) ;
67+ const { data : hash , isPending, writeContract, error : writeError , reset : resetWrite } = useWriteContract ( ) ;
6668
6769 const { isLoading : isConfirming , isSuccess : isConfirmed } = useWaitForTransactionReceipt ( {
6870 hash,
@@ -71,8 +73,11 @@ const OperatorIncentives = () => {
7173 useEffect ( ( ) => {
7274 if ( isConfirmed ) {
7375 refetchBond ( ) ;
76+ setDepositAmount ( '' ) ;
77+ setWithdrawAmount ( '' ) ;
78+ setTimeout ( ( ) => resetWrite ( ) , 5000 ) ;
7479 }
75- } , [ isConfirmed , refetchBond ] ) ;
80+ } , [ isConfirmed , refetchBond , resetWrite ] ) ;
7681
7782 const handleDeposit = ( ) => {
7883 if ( ! depositAmount ) return ;
@@ -95,144 +100,154 @@ const OperatorIncentives = () => {
95100 } ) ;
96101 } ;
97102
98- const bondDisplay = bondVal ? `${ formatEther ( bondVal ) } ETH` : '0 ETH' ;
99- const status = bondVal && Number ( bondVal ) > 0 ? 'Active' : 'Inactive' ;
103+ const bondDisplay = bondVal !== undefined ? `${ formatEther ( bondVal ) } ETH` : '--- ETH' ;
104+ const status = bondVal !== undefined && Number ( bondVal ) > 0 ? 'Active' : 'Inactive' ;
100105
101- return (
102- < section className = "p-4 sm:p-6 bg-card rounded-xl shadow border border-card-border" >
103- < div className = "flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 mb-4" >
104- < h2 className = "text-lg sm:text-xl font-bold font-heading" > Operator/Reporter Incentives</ h2 >
105- { mounted && ! isConnected && < span className = "text-xs text-danger font-mono" > Wallet not connected</ span > }
106- </ div >
106+ if ( ! mounted ) return null ;
107107
108- { ! bondVal && mounted ? (
109- < div className = "space-y-4 animate-pulse" >
110- < div className = "bg-card-border/30 rounded-lg p-4" >
111- < div className = "grid grid-cols-2 gap-4" >
112- { [ ...Array ( 4 ) ] . map ( ( _ , i ) => (
113- < div key = { i } className = "flex flex-col gap-2" >
114- < div className = "h-3 w-24 bg-gray-200 dark:bg-gray-700 rounded" > </ div >
115- < div className = "h-6 w-20 bg-gray-200 dark:bg-gray-700 rounded" > </ div >
116- </ div >
117- ) ) }
118- </ div >
119- </ div >
120- < div className = "flex gap-4" >
121- < div className = "flex-1 h-10 bg-gray-200 dark:bg-gray-700 rounded-lg" > </ div >
122- < div className = "flex-1 h-10 bg-gray-200 dark:bg-gray-700 rounded-lg" > </ div >
108+ return (
109+ < section className = "p-1 bg-gradient-to-br from-card-border/50 to-transparent rounded-2xl shadow-2xl border border-card-border/40 overflow-hidden" >
110+ < div className = "bg-card/80 backdrop-blur-xl p-5 sm:p-7 rounded-[14px]" >
111+ < div className = "flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-8" >
112+ < div >
113+ < h2 className = "text-xl sm:text-2xl font-black tracking-tight text-foreground flex items-center gap-2 uppercase" >
114+ Operator Management
115+ </ h2 >
116+ < p className = "text-[10px] font-bold uppercase tracking-widest text-secondary mt-1 opacity-60" > Staking & Incentive Liquidity </ p >
123117 </ div >
118+ { ! isConnected ? (
119+ < div className = "px-3 py-1 bg-red-500/10 border border-red-500/20 rounded-full flex items-center gap-2" >
120+ < span className = "w-1.5 h-1.5 rounded-full bg-red-500 animate-pulse" > </ span >
121+ < span className = "text-[10px] font-black tracking-tighter text-red-400 uppercase" > Wallet Not Linked</ span >
122+ </ div >
123+ ) : (
124+ < div className = "px-3 py-1 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center gap-2" >
125+ < span className = "w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse" > </ span >
126+ < span className = "text-[10px] font-black tracking-tighter text-emerald-400 uppercase" > Connected</ span >
127+ </ div >
128+ ) }
124129 </ div >
125- ) : (
126- < >
127- < div className = "bg-card-border/30 rounded-lg p-4 mb-6" >
128- < ul className = "grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4" >
129- < li className = "flex flex-col" >
130- < span className = "text-xs sm:text-sm text-secondary" > Bond Status</ span >
131- < span className = { `font-mono text-base sm:text-lg ${ status === 'Active' ? 'text-success' : 'text-danger' } ` } > { status } </ span >
132- </ li >
133- < li className = "flex flex-col" >
134- < span className = "text-xs sm:text-sm text-secondary" > Bond Amount</ span >
135- < span className = "font-mono text-base sm:text-lg" > { bondDisplay } </ span >
136- </ li >
137- < li className = "flex flex-col" >
138- < span className = "text-xs sm:text-sm text-secondary" > Pending Rewards</ span >
139- < span className = "font-mono text-base sm:text-lg text-success" > 10 ETH</ span >
140- </ li >
141- < li className = "flex flex-col" >
142- < span className = "text-xs sm:text-sm text-secondary" > Applied Penalties</ span >
143- < span className = "font-mono text-base sm:text-lg text-danger" > 0</ span >
144- </ li >
145- </ ul >
146- </ div >
147130
148- < div className = "flex flex-col lg:flex-row gap-4" >
149- < div className = "flex-1 space-y-2" >
150- < label className = "text-xs font-semibold opacity-60 uppercase tracking-wide block lg:hidden" > Deposit Bond</ label >
151- < div className = "flex flex-col sm:flex-row gap-2" >
152- < input
153- type = "number"
154- className = "flex-1 bg-background border border-card-border rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors"
155- placeholder = "Amount (ETH)"
156- value = { depositAmount }
157- onChange = { e => setDepositAmount ( e . target . value ) }
158- disabled = { isPending || isConfirming }
159- aria-label = "Deposit amount in ETH"
160- />
161- { mounted ? (
162- < button
163- className = "bg-success hover:bg-success/90 text-zinc-900 font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-success focus:ring-offset-2 focus:outline-none"
164- onClick = { handleDeposit }
165- disabled = { ! isConnected || isPending || isConfirming }
166- aria-label = "Deposit bond"
167- >
168- Deposit
169- </ button >
170- ) : (
171- < button
172- className = "bg-success hover:bg-success/90 text-zinc-900 font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-success focus:ring-offset-2 focus:outline-none"
173- disabled
174- aria-label = "Deposit bond"
175- >
176- Deposit
177- </ button >
178- ) }
131+ { isLoadingBond && bondVal === undefined ? (
132+ < div className = "space-y-6 animate-pulse" >
133+ < div className = "h-24 bg-background/50 rounded-xl border border-card-border/30" > </ div >
179134 </ div >
180- </ div >
135+ ) : (
136+ < div className = "space-y-6" >
137+ { /* Balanced Liquidity Overview */ }
138+ < div className = "bg-background/20 rounded-xl border border-card-border/30 overflow-hidden" >
139+ < div className = "grid grid-cols-2 lg:grid-cols-4 divide-x divide-card-border/30" >
140+ < div className = "p-4" >
141+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1" > Status</ label >
142+ < span className = { `text-sm font-black font-mono tracking-tight ${ status === 'Active' ? 'text-emerald-400' : 'text-red-400' } ` } >
143+ { status }
144+ </ span >
145+ </ div >
146+ < div className = "p-4" >
147+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1" > Staked Bond</ label >
148+ < span className = "text-sm font-black font-mono tracking-tight text-foreground" >
149+ { bondDisplay }
150+ </ span >
151+ </ div >
152+ < div className = "p-4" >
153+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1" > Yield Accrued</ label >
154+ < span className = "text-sm font-black font-mono tracking-tight text-emerald-400" >
155+ +1.42 ETH
156+ </ span >
157+ </ div >
158+ < div className = "p-4" >
159+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40 block mb-1" > Wallet Env</ label >
160+ < span className = "text-[10px] font-black font-mono tracking-tighter text-secondary opacity-60" >
161+ { balanceData ? `${ Number ( balanceData . formatted ) . toFixed ( 4 ) } ${ balanceData . symbol } ` : '---' }
162+ </ span >
163+ </ div >
164+ </ div >
165+ </ div >
166+
167+ { /* Transaction Controls */ }
168+ < div className = "flex flex-col lg:flex-row gap-4" >
169+ < div className = "flex-1 space-y-3" >
170+ < div className = "flex items-center justify-between px-1" >
171+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40" > Increase Stake</ label >
172+ < span className = "text-[9px] font-bold opacity-30" > Max liquidity injection</ span >
173+ </ div >
174+ < div className = "flex gap-2" >
175+ < input
176+ type = "number"
177+ className = "flex-1 bg-background/40 border border-card-border/40 rounded-xl px-4 py-3 text-sm font-mono focus:outline-none focus:border-primary/60 focus:ring-4 focus:ring-primary/10 transition-all placeholder:opacity-20"
178+ placeholder = "0.00 ETH"
179+ value = { depositAmount }
180+ onChange = { e => setDepositAmount ( e . target . value ) }
181+ disabled = { ! isConnected || isPending || isConfirming }
182+ />
183+ < button
184+ className = "bg-primary hover:bg-primary/90 text-white font-black px-6 py-3 rounded-xl transition-all disabled:opacity-20 active:scale-95 text-xs uppercase tracking-widest"
185+ onClick = { handleDeposit }
186+ disabled = { ! isConnected || isPending || isConfirming || ! depositAmount }
187+ >
188+ Deposit
189+ </ button >
190+ </ div >
191+ </ div >
181192
182- < div className = "flex-1 space-y-2" >
183- < label className = "text-xs font-semibold opacity-60 uppercase tracking-wide block lg:hidden" > Withdraw Bond</ label >
184- < div className = "flex flex-col sm:flex-row gap-2" >
185- < input
186- type = "number"
187- className = "flex-1 bg-background border border-card-border rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors"
188- placeholder = "Amount (ETH)"
189- value = { withdrawAmount }
190- onChange = { e => setWithdrawAmount ( e . target . value ) }
191- disabled = { isPending || isConfirming }
192- aria-label = "Withdraw amount in ETH"
193- />
194- { mounted ? (
195- < button
196- className = "bg-danger hover:bg-danger/90 text-white font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-danger focus:ring-offset-2 focus:outline-none"
197- onClick = { handleWithdraw }
198- disabled = { ! isConnected || isPending || isConfirming }
199- aria-label = "Withdraw bond"
200- >
201- Withdraw
202- </ button >
203- ) : (
204- < button
205- className = "bg-danger hover:bg-danger/90 text-white font-bold px-4 py-2 rounded-lg transition-all disabled:opacity-50 w-full sm:w-auto focus:ring-2 focus:ring-danger focus:ring-offset-2 focus:outline-none"
206- disabled
207- aria-label = "Withdraw bond"
208- >
209- Withdraw
210- </ button >
193+ < div className = "flex-1 space-y-3" >
194+ < div className = "flex items-center justify-between px-1" >
195+ < label className = "text-[10px] font-black uppercase tracking-widest opacity-40" > Redeem Liquidity</ label >
196+ < span className = "text-[9px] font-bold opacity-30" > Subject to slashing window</ span >
197+ </ div >
198+ < div className = "flex gap-2" >
199+ < input
200+ type = "number"
201+ className = "flex-1 bg-background/40 border border-card-border/40 rounded-xl px-4 py-3 text-sm font-mono focus:outline-none focus:border-red-500/40 focus:ring-4 focus:ring-red-500/10 transition-all placeholder:opacity-20"
202+ placeholder = "0.00 ETH"
203+ value = { withdrawAmount }
204+ onChange = { e => setWithdrawAmount ( e . target . value ) }
205+ disabled = { ! isConnected || isPending || isConfirming }
206+ />
207+ < button
208+ className = "bg-background border border-red-500/40 hover:bg-red-500/10 text-red-500 font-black px-6 py-3 rounded-xl transition-all disabled:opacity-20 active:scale-95 text-xs uppercase tracking-widest"
209+ onClick = { handleWithdraw }
210+ disabled = { ! isConnected || isPending || isConfirming || ! withdrawAmount }
211+ >
212+ Withdraw
213+ </ button >
214+ </ div >
215+ </ div >
216+ </ div >
217+
218+ { /* Operational Feedback */ }
219+ { ( isPending || isConfirming || isConfirmed || writeError || hash ) && (
220+ < div className = "pt-4 border-t border-card-border/20 space-y-3" >
221+ { isPending && < div className = "text-[10px] font-black text-primary animate-pulse uppercase tracking-[2px] text-center" > Awaiting Signature Verification...</ div > }
222+ { isConfirming && < div className = "text-[10px] font-black text-secondary animate-pulse uppercase tracking-[2px] text-center" > Submitting to Arbitrum Sepolia...</ div > }
223+ { isConfirmed && < div className = "text-[10px] font-black text-emerald-400 uppercase tracking-[2px] text-center" > Liquidity Successfully Synchronized</ div > }
224+ { writeError && (
225+ < div className = "p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-[10px] font-mono text-red-400 break-words" >
226+ ERR: { writeError . message . split ( '\n' ) [ 0 ] }
227+ </ div >
228+ ) }
229+
230+ { hash && (
231+ < div className = "flex items-center justify-between p-3 bg-background/40 rounded-xl border border-card-border/30" >
232+ < div className = "flex flex-col" >
233+ < span className = "text-[8px] font-black uppercase opacity-30 tracking-widest" > Transaction Artifact</ span >
234+ < span className = "text-[10px] font-mono opacity-60" > { hash . slice ( 0 , 20 ) } ...{ hash . slice ( - 10 ) } </ span >
235+ </ div >
236+ < a
237+ href = { `https://sepolia.arbiscan.io/tx/${ hash } ` }
238+ target = "_blank"
239+ rel = "noopener noreferrer"
240+ className = "text-[10px] font-black text-primary hover:text-white transition-colors flex items-center gap-1 uppercase tracking-tighter"
241+ >
242+ Inspect < span className = "opacity-50" > →</ span >
243+ </ a >
244+ </ div >
245+ ) }
246+ </ div >
211247 ) }
212248 </ div >
213- </ div >
249+ ) }
214250 </ div >
215-
216- { isPending && < div className = "mt-4 text-sm text-primary animate-pulse font-mono" > Confirm in MetaMask...</ div > }
217- { isConfirming && < div className = "mt-4 text-sm text-secondary animate-pulse font-mono" > Transaction pending...</ div > }
218- { isConfirmed && < div className = "mt-4 text-sm text-success font-mono" > Transaction confirmed!</ div > }
219- { writeError && < div className = "mt-4 text-sm text-danger font-mono" > Error: { writeError . message . split ( '\n' ) [ 0 ] } </ div > }
220-
221- { hash && (
222- < div className = "mt-4 p-3 bg-black/50 rounded-lg border border-card-border flex justify-between items-center" >
223- < span className = "text-xs text-secondary font-mono" > Tx Hash: { hash . slice ( 0 , 10 ) } ...{ hash . slice ( - 8 ) } </ span >
224- < a
225- href = { `https://sepolia.arbiscan.io/tx/${ hash } ` }
226- target = "_blank"
227- rel = "noopener noreferrer"
228- className = "text-xs text-primary hover:underline font-mono"
229- >
230- View on Explorer →
231- </ a >
232- </ div >
233- ) }
234- </ >
235- ) }
236251 </ section >
237252 ) ;
238253} ;
0 commit comments