@@ -6,15 +6,19 @@ import Skeleton from "../components/Skeleton";
66import Tabs from "../components/Tabs" ;
77import ContractInteraction from "../components/ContractInteraction" ;
88import DecodedTransactionInput from "../components/DecodedTransactionInput" ;
9- import { fetchAddressByAddress , fetchAddressTransactions } from "../lib/BlockchainApi" ;
9+ import { fetchAddressByAddress , fetchAddressTransactions , fetchERC20Transfers , fetchInternalTransactions } from "../lib/BlockchainApi" ;
1010import { parseABI } from "../lib/AbiDecoder" ;
11- import { Copy , Edit2 , FileCode , Tag , Wallet , X } from "lucide-react" ;
11+ import { ArrowDownLeft , ArrowUpRight , Bookmark , BookmarkCheck , Copy , Edit2 , FileCode , GitBranch , Tag , Trash2 , Wallet , X } from "lucide-react" ;
1212
1313export default function AddressDetailPage ( { address } ) {
1414 const [ addressData , setAddressData ] = useState ( null ) ;
1515 const [ transactions , setTransactions ] = useState ( [ ] ) ;
16+ const [ erc20Transfers , setErc20Transfers ] = useState ( [ ] ) ;
17+ const [ internalTxs , setInternalTxs ] = useState ( [ ] ) ;
1618 const [ isLoadingAddress , setIsLoadingAddress ] = useState ( true ) ;
1719 const [ isLoadingTxs , setIsLoadingTxs ] = useState ( true ) ;
20+ const [ isLoadingErc20 , setIsLoadingErc20 ] = useState ( true ) ;
21+ const [ isLoadingInternal , setIsLoadingInternal ] = useState ( true ) ;
1822 const {
1923 rpcUrl,
2024 getContractABI,
@@ -23,6 +27,9 @@ export default function AddressDetailPage({ address }) {
2327 getAddressLabel,
2428 saveAddressLabel,
2529 removeAddressLabel,
30+ isAddressSaved,
31+ saveAddress,
32+ unsaveAddress,
2633 } = useBlockchain ( ) ;
2734 const { navigate } = useRouter ( ) ;
2835 const [ copiedAddress , setCopiedAddress ] = useState ( false ) ;
@@ -36,6 +43,8 @@ export default function AddressDetailPage({ address }) {
3643 const [ showLabelInput , setShowLabelInput ] = useState ( false ) ;
3744 const [ labelInput , setLabelInput ] = useState ( "" ) ;
3845
46+ const isSaved = isAddressSaved ( address ) ;
47+
3948 useEffect ( ( ) => {
4049 const savedABI = getContractABI ( address ) ;
4150 if ( savedABI ) {
@@ -77,6 +86,38 @@ export default function AddressDetailPage({ address }) {
7786 loadTransactions ( ) ;
7887 } , [ rpcUrl , address ] ) ;
7988
89+ // TODO: needed/used?
90+ useEffect ( ( ) => {
91+ const loadErc20Transfers = async ( ) => {
92+ if ( ! rpcUrl || ! address ) return ;
93+ try {
94+ const data = await fetchERC20Transfers ( rpcUrl , address ) ;
95+ setErc20Transfers ( data ) ;
96+ } catch ( error ) {
97+ console . error ( "Error fetching ERC20 transfers:" , error ) ;
98+ } finally {
99+ setIsLoadingErc20 ( false ) ;
100+ }
101+ } ;
102+ loadErc20Transfers ( ) ;
103+ } , [ rpcUrl , address ] ) ;
104+
105+ // TODO: needed/used?
106+ useEffect ( ( ) => {
107+ const loadInternalTxs = async ( ) => {
108+ if ( ! rpcUrl || ! address ) return ;
109+ try {
110+ const data = await fetchInternalTransactions ( rpcUrl , address ) ;
111+ setInternalTxs ( data ) ;
112+ } catch ( error ) {
113+ console . error ( "Error fetching internal transactions:" , error ) ;
114+ } finally {
115+ setIsLoadingInternal ( false ) ;
116+ }
117+ } ;
118+ loadInternalTxs ( ) ;
119+ } , [ rpcUrl , address ] ) ;
120+
80121 const copyToClipboard = ( text , field = null ) => {
81122 navigator . clipboard . writeText ( text ) ;
82123 if ( field ) {
@@ -88,6 +129,14 @@ export default function AddressDetailPage({ address }) {
88129 }
89130 } ;
90131
132+ const handleToggleSave = ( ) => {
133+ if ( isSaved ) {
134+ unsaveAddress ( address ) ;
135+ } else {
136+ saveAddress ( address ) ;
137+ }
138+ } ;
139+
91140 const handleParseABI = ( ) => {
92141 try {
93142 setAbiError ( "" ) ;
@@ -374,20 +423,10 @@ export default function AddressDetailPage({ address }) {
374423 } `}
375424 >
376425 { addressData . isContract
377- ? (
378- < >
379- < FileCode className = "w-5 h-5" />
380- Contract
381- </ >
382- )
383- : (
384- < >
385- < Wallet className = "w-5 h-5" />
386- Wallet
387- </ >
388- ) }
389- </ span >
390- ) }
426+ ? ( < > < FileCode className = "w-5 h-5" /> Contract </ > )
427+ : ( < > < Wallet className = "w-5 h-5" /> Wallet </ > ) }
428+ </ span >
429+ ) }
391430 </ div >
392431
393432 < div className = "float-right" >
@@ -397,64 +436,75 @@ export default function AddressDetailPage({ address }) {
397436 < Tag className = "w-4 h-4 text-[oklch(0.65_0.25_151)]" />
398437 < span className = "text-foreground font-medium" > { label } </ span >
399438 </ div >
400- < button
401- onClick = { handleEditLabel }
402- className = "p-2 text-muted-foreground hover:text-foreground transition-colors"
403- title = "Edit label"
404- >
405- < Edit2 className = "w-4 h-4" />
406- </ button >
407- < button
408- onClick = { handleRemoveLabel }
439+ < button
440+ onClick = { handleEditLabel }
441+ className = "p-2 text-muted-foreground hover:text-foreground transition-colors"
442+ title = "Edit label"
443+ >
444+ < Edit2 className = "w-4 h-4" />
445+ </ button >
446+ < button
447+ onClick = { handleRemoveLabel }
409448 className = "p-2 text-muted-foreground hover:text-red-500 transition-colors"
410449 title = "Remove label"
411- >
450+ >
412451 < X className = "w-4 h-4" />
413- </ button >
414- </ div >
415- ) }
452+ </ button >
453+ < button
454+ onClick = { handleToggleSave }
455+ className = { `btn ${
456+ isSaved ? "btn-primary" : "btn-outline"
457+ } flex items-center gap-2`}
458+ >
459+ { isSaved
460+ ? ( < > < BookmarkCheck className = "w-4 h-4" /> Saved </ > )
461+ : ( < > < Bookmark className = "w-4 h-4" /> Save </ > )
462+ }
463+ </ button >
464+ </ div >
465+ ) }
416466
417- { showLabelInput && (
467+ { showLabelInput && (
418468 < div className = "mb-6 flex items-center gap-2" >
419- < input
420- type = "text"
421- value = { labelInput }
422- onChange = { ( e ) => setLabelInput ( e . target . value ) }
469+ < input
470+ type = "text"
471+ value = { labelInput }
472+ onChange = { ( e ) => setLabelInput ( e . target . value ) }
423473 placeholder = "Enter a label or note for this address..."
424474 className = "input flex-1 max-w-md"
425- onKeyDown = { ( e ) => e . key === "Enter" && handleSaveLabel ( ) }
426- autoFocus
427- />
475+ onKeyDown = { ( e ) => e . key === "Enter" && handleSaveLabel ( ) }
476+ autoFocus
477+ />
428478 < button onClick = { handleSaveLabel } className = "btn btn-primary" >
429- Save
430- </ button >
431- < button
432- onClick = { ( ) => {
433- setShowLabelInput ( false ) ;
434- setLabelInput ( "" ) ;
435- } }
479+ Save
480+ </ button >
481+ < button
482+ onClick = { ( ) => {
483+ setShowLabelInput ( false ) ;
484+ setLabelInput ( "" ) ;
485+ } }
436486 className = "btn btn-outline"
437- >
438- Cancel
439- </ button >
440- </ div >
441- ) }
487+ >
488+ Cancel
489+ </ button >
490+ </ div >
491+ ) }
442492
443- { ! label && ! showLabelInput && (
493+ { ! label && ! showLabelInput && (
444494 < div className = "mb-6" >
445- < button
446- onClick = { ( ) => setShowLabelInput ( true ) }
495+ < button
496+ onClick = { ( ) => setShowLabelInput ( true ) }
447497 className = "flex items-center gap-2 text-muted-foreground hover:text-[oklch(0.65_0.25_151)] transition-colors text-sm"
448- >
498+ >
449499 < Tag className = "w-4 h-4" />
450- Add label
451- </ button >
452- </ div >
453- ) }
454- </ div >
500+ Add label
501+ </ button >
502+ </ div >
503+ ) }
504+ </ div >
455505
456506 < Tabs tabs = { tabs } defaultTab = "overview" />
457- </ div >
507+ </ div >
458508 </ main >
459509 < Footer />
460510 </ div >
0 commit comments