@@ -6,17 +6,17 @@ import {
66 TableContainer , TableHead , TableRow , TextField , Button , Alert ,
77 CircularProgress ,
88} from "@mui/material" ;
9- import { useAccount , useReadContract } from "wagmi" ;
9+ import { useAccount , useReadContract , useReadContracts } from "wagmi" ;
1010import { useSearchParams } from "next/navigation" ;
1111import { zeroAddress } from "viem" ;
1212import { CONTRACTS , REWARDS_PROGRAM_ABI , MemberRoleLabels } from "@/config/contracts" ;
13- import { toBytes12 , fromBytes12 , fromBytes8 , shortenAddress , formatFula , formatDate } from "@/lib/utils" ;
13+ import { toBytes12 , fromBytes12 , fromBytes8 , shortenAddress , formatFula , formatDate , formatContractError } from "@/lib/utils" ;
1414import { useProgramCount , useProgram , useTimeLocks , useDirectChildren , useTransferToParent , useWithdraw , useApproveToken , useAddTokens } from "@/hooks/useRewardsProgram" ;
1515import { OnChainDisclaimer } from "@/components/common/OnChainDisclaimer" ;
1616
17- /* ── Per-program membership row ── */
17+ /* -- Per-program membership row -- */
1818
19- function MemberProgramRow ( { memberID, programId, isOwner } : { memberID : string ; programId : number ; isOwner : boolean } ) {
19+ function MemberProgramRow ( { memberID, programId } : { memberID : string ; programId : number } ) {
2020 const memberIDBytes = toBytes12 ( memberID ) ;
2121
2222 const { data : member } = useReadContract ( {
@@ -37,7 +37,6 @@ function MemberProgramRow({ memberID, programId, isOwner }: { memberID: string;
3737 query : { enabled : ! ! member ?. wallet && member . wallet !== zeroAddress } ,
3838 } ) ;
3939
40- // Don't render if member doesn't exist in this program
4140 if ( ! member || ! member . wallet || member . wallet === zeroAddress ) return null ;
4241
4342 return (
@@ -57,9 +56,9 @@ function MemberProgramRow({ memberID, programId, isOwner }: { memberID: string;
5756 ) ;
5857}
5958
60- /* ── Actions panel (only for wallet owner) ── */
59+ /* -- Actions panel (only for wallet owner) -- */
6160
62- function OwnerActions ( { memberID , memberWallet } : { memberID : string ; memberWallet : string } ) {
61+ function OwnerActions ( { memberWallet } : { memberWallet : string } ) {
6362 const { address } = useAccount ( ) ;
6463 const isOwner = address ?. toLowerCase ( ) === memberWallet . toLowerCase ( ) ;
6564
@@ -98,7 +97,7 @@ function OwnerActions({ memberID, memberWallet }: { memberID: string; memberWall
9897 fullWidth size = "small" type = "number" />
9998 < Box sx = { { display : "flex" , gap : 1 , mt : 1 } } >
10099 < Button size = "small" variant = "outlined" onClick = { ( ) => approve ( depositAmount ) }
101- disabled = { isApproving || isAppConf || ! depositAmount } >
100+ disabled = { isApproving || isAppConf || ! depositAmount || ! disclaimer } >
102101 { isApproving || isAppConf ? < CircularProgress size = { 16 } /> : "Approve" }
103102 </ Button >
104103 < Button size = "small" variant = "contained" onClick = { ( ) => addTokens ( pid , depositAmount ) }
@@ -108,7 +107,7 @@ function OwnerActions({ memberID, memberWallet }: { memberID: string; memberWall
108107 </ Box >
109108 { approveSuccess && < Alert severity = "info" sx = { { mt : 1 } } > Approved. Now click Deposit.</ Alert > }
110109 { depositSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Deposited!</ Alert > }
111- { depositError && < Alert severity = "error" sx = { { mt : 1 } } > { depositError . message } </ Alert > }
110+ { depositError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( depositError ) } </ Alert > }
112111 </ Grid >
113112
114113 { /* Transfer to Parent */ }
@@ -124,7 +123,7 @@ function OwnerActions({ memberID, memberWallet }: { memberID: string; memberWall
124123 { isTransBack || isTransBackConf ? < CircularProgress size = { 16 } /> : "Transfer" }
125124 </ Button >
126125 { transBackSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Transferred!</ Alert > }
127- { transBackError && < Alert severity = "error" sx = { { mt : 1 } } > { transBackError . message } </ Alert > }
126+ { transBackError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( transBackError ) } </ Alert > }
128127 </ Grid >
129128
130129 { /* Withdraw */ }
@@ -138,7 +137,7 @@ function OwnerActions({ memberID, memberWallet }: { memberID: string; memberWall
138137 { isWithdrawing || isWithConf ? < CircularProgress size = { 16 } /> : "Withdraw" }
139138 </ Button >
140139 { withdrawSuccess && < Alert severity = "success" sx = { { mt : 1 } } > Withdrawn!</ Alert > }
141- { withdrawError && < Alert severity = "error" sx = { { mt : 1 } } > { withdrawError . message } </ Alert > }
140+ { withdrawError && < Alert severity = "error" sx = { { mt : 1 } } > { formatContractError ( withdrawError ) } </ Alert > }
142141 </ Grid >
143142 </ Grid >
144143
@@ -147,7 +146,7 @@ function OwnerActions({ memberID, memberWallet }: { memberID: string; memberWall
147146 ) ;
148147}
149148
150- /* ── Time Locks detail ── */
149+ /* -- Time Locks detail -- */
151150
152151function TimeLockDetails ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
153152 const { data : timeLocks } = useTimeLocks ( programId , wallet ) ;
@@ -165,7 +164,7 @@ function TimeLockDetails({ programId, wallet }: { programId: number; wallet: `0x
165164 ) ;
166165}
167166
168- /* ── Sub-member row ── */
167+ /* -- Sub-member row -- */
169168
170169function SubMemberRow ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
171170 const { data : member } = useReadContract ( {
@@ -199,7 +198,7 @@ function SubMemberRow({ programId, wallet }: { programId: number; wallet: `0x${s
199198 ) ;
200199}
201200
202- /* ── Sub-members for a program ── */
201+ /* -- Sub-members for a program -- */
203202
204203function SubMembersSection ( { programId, wallet } : { programId : number ; wallet : `0x${string } ` } ) {
205204 const { data : children } = useDirectChildren ( programId , wallet ) ;
@@ -236,7 +235,7 @@ function SubMembersSection({ programId, wallet }: { programId: number; wallet: `
236235 ) ;
237236}
238237
239- /* ── Main balance view ── */
238+ /* -- Main balance view -- */
240239
241240function BalanceContent ( ) {
242241 const searchParams = useSearchParams ( ) ;
@@ -247,37 +246,33 @@ function BalanceContent() {
247246 const { data : programCount } = useProgramCount ( ) ;
248247 const count = Number ( programCount || 0 ) ;
249248
250- // Look up the member in program 1 to get their wallet (for ownership check)
249+ // Dynamic wallet discovery: multicall getMemberByID across all programs
251250 const memberIDBytes = toBytes12 ( searchID ) ;
252- const { data : firstMatch } = useReadContract ( {
251+ const contracts = Array . from ( { length : count } , ( _ , i ) => ( {
253252 address : CONTRACTS . rewardsProgram ,
254253 abi : REWARDS_PROGRAM_ABI ,
255- functionName : "getMemberByID" ,
256- args : [ memberIDBytes , 1 ] ,
257- query : { enabled : ! ! searchID && count > 0 } ,
258- } ) ;
254+ functionName : "getMemberByID" as const ,
255+ args : [ memberIDBytes , i + 1 ] as const ,
256+ } ) ) ;
259257
260- // Try to find the wallet by iterating if program 1 didn't have it
261- // We'll check up to the first few programs for the wallet address
262- const { data : match2 } = useReadContract ( {
263- address : CONTRACTS . rewardsProgram ,
264- abi : REWARDS_PROGRAM_ABI ,
265- functionName : "getMemberByID" ,
266- args : [ memberIDBytes , 2 ] ,
267- query : { enabled : ! ! searchID && count >= 2 && ( ! firstMatch ?. wallet || firstMatch . wallet === zeroAddress ) } ,
268- } ) ;
269- const { data : match3 } = useReadContract ( {
270- address : CONTRACTS . rewardsProgram ,
271- abi : REWARDS_PROGRAM_ABI ,
272- functionName : "getMemberByID" ,
273- args : [ memberIDBytes , 3 ] ,
274- query : { enabled : ! ! searchID && count >= 3 && ( ! firstMatch ?. wallet || firstMatch . wallet === zeroAddress ) && ( ! match2 ?. wallet || match2 . wallet === zeroAddress ) } ,
258+ const { data : multicallResults } = useReadContracts ( {
259+ contracts : contracts . length > 0 ? contracts : undefined ,
260+ query : { enabled : ! ! searchID && count > 0 } ,
275261 } ) ;
276262
277- const memberWallet =
278- ( firstMatch ?. wallet && firstMatch . wallet !== zeroAddress ? firstMatch . wallet :
279- match2 ?. wallet && match2 . wallet !== zeroAddress ? match2 . wallet :
280- match3 ?. wallet && match3 . wallet !== zeroAddress ? match3 . wallet : "" ) as string ;
263+ // Find the first result that has a valid wallet
264+ let memberWallet = "" ;
265+ if ( multicallResults ) {
266+ for ( const result of multicallResults ) {
267+ if ( result . status === "success" && result . result ) {
268+ const member = result . result as { wallet : string } ;
269+ if ( member . wallet && member . wallet !== zeroAddress ) {
270+ memberWallet = member . wallet ;
271+ break ;
272+ }
273+ }
274+ }
275+ }
281276
282277 const handleSearch = ( ) => {
283278 setSearchID ( memberID ) ;
@@ -313,45 +308,51 @@ function BalanceContent() {
313308 </ Paper >
314309 ) }
315310
316- < Paper sx = { { p : 2 } } >
317- < Typography variant = "h6" gutterBottom > Programs & Balances for & quot ; { searchID } "</ Typography >
318- < Typography variant = "caption" color = "text.secondary" sx = { { mb : 2 , display : "block" } } >
319- Balance columns: Withdrawable / Permanently Locked / Time-Locked (FULA)
320- </ Typography >
321- < TableContainer >
322- < Table size = "small" >
323- < TableHead >
324- < TableRow >
325- < TableCell > Program</ TableCell >
326- < TableCell > Code</ TableCell >
327- < TableCell > Role</ TableCell >
328- < TableCell > Parent</ TableCell >
329- < TableCell > Withdrawable</ TableCell >
330- < TableCell > Locked</ TableCell >
331- < TableCell > Time-Locked</ TableCell >
332- < TableCell > Status</ TableCell >
333- </ TableRow >
334- </ TableHead >
335- < TableBody >
336- { Array . from ( { length : count } , ( _ , i ) => (
337- < MemberProgramRow key = { i + 1 } memberID = { searchID } programId = { i + 1 } isOwner = { false } />
338- ) ) }
339- </ TableBody >
340- </ Table >
341- </ TableContainer >
342- </ Paper >
343-
344- { /* Sub-members per program */ }
345- { memberWallet && (
346- < Paper sx = { { p : 2 , mt : 3 } } >
347- < Typography variant = "h6" gutterBottom > Sub-Members</ Typography >
348- { Array . from ( { length : count } , ( _ , i ) => (
349- < SubMembersSection key = { i + 1 } programId = { i + 1 } wallet = { memberWallet as `0x${string } `} />
350- ) ) }
351- </ Paper >
311+ { ! memberWallet && (
312+ < Alert severity = "warning" sx = { { mb : 3 } } > No member found with ID "{ searchID } " in any program.</ Alert >
352313 ) }
353314
354- { memberWallet && < OwnerActions memberID = { searchID } memberWallet = { memberWallet } /> }
315+ { memberWallet && (
316+ < >
317+ < Paper sx = { { p : 2 } } >
318+ < Typography variant = "h6" gutterBottom > Programs & Balances for & quot ; { searchID } "</ Typography >
319+ < Typography variant = "caption" color = "text.secondary" sx = { { mb : 2 , display : "block" } } >
320+ Balance columns: Withdrawable / Permanently Locked / Time-Locked (FULA)
321+ </ Typography >
322+ < TableContainer >
323+ < Table size = "small" >
324+ < TableHead >
325+ < TableRow >
326+ < TableCell > Program</ TableCell >
327+ < TableCell > Code</ TableCell >
328+ < TableCell > Role</ TableCell >
329+ < TableCell > Parent</ TableCell >
330+ < TableCell > Withdrawable</ TableCell >
331+ < TableCell > Locked</ TableCell >
332+ < TableCell > Time-Locked</ TableCell >
333+ < TableCell > Status</ TableCell >
334+ </ TableRow >
335+ </ TableHead >
336+ < TableBody >
337+ { Array . from ( { length : count } , ( _ , i ) => (
338+ < MemberProgramRow key = { i + 1 } memberID = { searchID } programId = { i + 1 } />
339+ ) ) }
340+ </ TableBody >
341+ </ Table >
342+ </ TableContainer >
343+ </ Paper >
344+
345+ { /* Sub-members per program */ }
346+ < Paper sx = { { p : 2 , mt : 3 } } >
347+ < Typography variant = "h6" gutterBottom > Sub-Members</ Typography >
348+ { Array . from ( { length : count } , ( _ , i ) => (
349+ < SubMembersSection key = { i + 1 } programId = { i + 1 } wallet = { memberWallet as `0x${string } `} />
350+ ) ) }
351+ </ Paper >
352+
353+ < OwnerActions memberWallet = { memberWallet } />
354+ </ >
355+ ) }
355356 </ >
356357 ) }
357358
0 commit comments