11import './NFTList.css' ;
22
3+ import type { PrimaryWallet } from '@zetachain/wallet' ;
34import { useEffect , useState } from 'react' ;
45
6+ import { SUPPORTED_CHAINS } from '../constants/chains' ;
7+ import { NFT_CONTRACT_ADDRESSES } from '../constants/contracts' ;
8+ import type { EIP6963ProviderDetail } from '../types/wallet' ;
9+ import { getSignerAndProvider } from '../utils/ethersHelpers' ;
510import { getAllNFTsForDisplay } from '../utils/nftDisplay' ;
11+ import { transferNFT } from '../utils/nftTransfer' ;
612
713interface NFTDisplayData {
814 id : string ;
@@ -19,9 +25,16 @@ interface NFTDisplayData {
1925}
2026
2127
22- export function NFTList ( ) {
28+ interface NFTListProps {
29+ selectedProvider : EIP6963ProviderDetail | null ;
30+ primaryWallet : PrimaryWallet | null ;
31+ }
32+
33+ export function NFTList ( { selectedProvider, primaryWallet } : NFTListProps ) {
2334 const [ nfts , setNfts ] = useState < NFTDisplayData [ ] > ( [ ] ) ;
2435 const [ loading , setLoading ] = useState ( true ) ;
36+ const [ selectedDestinations , setSelectedDestinations ] = useState < Record < string , string > > ( { } ) ;
37+ const [ transferring , setTransferring ] = useState < Record < string , boolean > > ( { } ) ;
2538
2639 useEffect ( ( ) => {
2740 const loadNFTs = ( ) => {
@@ -43,6 +56,98 @@ export function NFTList() {
4356 return ( ) => clearInterval ( interval ) ;
4457 } , [ ] ) ;
4558
59+ const handleDestinationChange = ( nftId : string , destination : string ) => {
60+ setSelectedDestinations ( prev => ( {
61+ ...prev ,
62+ [ nftId ] : destination
63+ } ) ) ;
64+ } ;
65+
66+ const handleTransfer = async ( nft : NFTDisplayData ) => {
67+ const nftKey = `${ nft . contractAddress } _${ nft . id } ` ;
68+ const destination = selectedDestinations [ nftKey ] ;
69+
70+ if ( ! destination ) {
71+ alert ( 'Please select a destination chain' ) ;
72+ return ;
73+ }
74+
75+ setTransferring ( prev => ( { ...prev , [ nftKey ] : true } ) ) ;
76+
77+ try {
78+ console . log ( `Transferring NFT ${ nft . id } from ${ nft . currentChain } to ${ destination } ` ) ;
79+
80+ // Get signer and provider
81+ const signerAndProvider = await getSignerAndProvider ( {
82+ selectedProvider,
83+ primaryWallet,
84+ } ) ;
85+
86+ if ( ! signerAndProvider ) {
87+ throw new Error ( 'Failed to get wallet connection' ) ;
88+ }
89+
90+ const { signer } = signerAndProvider ;
91+ const userAddress = await signer . getAddress ( ) ;
92+
93+ // Confirm transfer
94+ const confirmed = confirm (
95+ `Transfer NFT #${ nft . id } from ${ nft . currentChain } to ${ destination } ?\n\n` +
96+ `This will initiate a cross-chain transfer. The NFT will be moved to the destination chain.\n\n` +
97+ `You will need to approve two transactions:\n` +
98+ `1. Approve the contract to transfer your NFT\n` +
99+ `2. Execute the cross-chain transfer`
100+ ) ;
101+
102+ if ( confirmed ) {
103+ // Get the contract address for the current chain
104+ const currentChainContractAddress = getNFTContractAddress ( nft . currentChain ) ;
105+ if ( ! currentChainContractAddress ) {
106+ throw new Error ( `Contract address not found for current chain: ${ nft . currentChain } ` ) ;
107+ }
108+
109+ // Execute the actual transfer
110+ const result = await transferNFT ( {
111+ tokenId : nft . id ,
112+ contractAddress : nft . contractAddress , // Original contract address for storage
113+ currentChainContractAddress : currentChainContractAddress , // Current chain contract for approve/transfer
114+ destinationChain : destination ,
115+ receiverAddress : userAddress ,
116+ signer : signer ,
117+ fromChainName : nft . currentChain ,
118+ } ) ;
119+
120+ alert ( `Transfer successful! NFT #${ nft . id } is being transferred to ${ destination } .\n\nTransaction hash: ${ result . hash } ` ) ;
121+
122+ // Refresh the NFT list to show updated state
123+ const nftData = getAllNFTsForDisplay ( ) ;
124+ setNfts ( nftData ) ;
125+ }
126+ } catch ( error ) {
127+ console . error ( 'Transfer failed:' , error ) ;
128+ alert ( `Transfer failed: ${ error instanceof Error ? error . message : 'Unknown error' } ` ) ;
129+ } finally {
130+ setTransferring ( prev => ( { ...prev , [ nftKey ] : false } ) ) ;
131+ }
132+ } ;
133+
134+ const getAvailableDestinations = ( currentChain : string ) => {
135+ return SUPPORTED_CHAINS . filter ( chain => chain . name !== currentChain ) ;
136+ } ;
137+
138+ const getNFTContractAddress = ( chainName : string ) : string | null => {
139+ switch ( chainName ) {
140+ case 'ZetaChain' :
141+ return NFT_CONTRACT_ADDRESSES . ZETACHAIN ;
142+ case 'Ethereum Sepolia' :
143+ return NFT_CONTRACT_ADDRESSES . SEPOLIA ;
144+ case 'Base Sepolia' :
145+ return NFT_CONTRACT_ADDRESSES . BASE_SEPOLIA ;
146+ default :
147+ return null ;
148+ }
149+ } ;
150+
46151 if ( loading ) {
47152 return (
48153 < div className = "nft-list-container" >
@@ -87,14 +192,27 @@ export function NFTList() {
87192 >
88193 View Metadata
89194 </ a >
90- { /* Future: Add transfer button here */ }
91- < button
92- className = "nft-transfer-btn"
93- disabled
94- title = "Cross-chain transfer coming soon"
95- >
96- Transfer
97- </ button >
195+ < div className = "nft-transfer-section" >
196+ < select
197+ className = "nft-destination-select"
198+ value = { selectedDestinations [ `${ nft . contractAddress } _${ nft . id } ` ] || '' }
199+ onChange = { ( e ) => handleDestinationChange ( `${ nft . contractAddress } _${ nft . id } ` , e . target . value ) }
200+ >
201+ < option value = "" > Select destination</ option >
202+ { getAvailableDestinations ( nft . currentChain ) . map ( chain => (
203+ < option key = { chain . name } value = { chain . name } >
204+ { chain . name }
205+ </ option >
206+ ) ) }
207+ </ select >
208+ < button
209+ className = "nft-transfer-btn"
210+ onClick = { ( ) => handleTransfer ( nft ) }
211+ disabled = { transferring [ `${ nft . contractAddress } _${ nft . id } ` ] || ! selectedDestinations [ `${ nft . contractAddress } _${ nft . id } ` ] }
212+ >
213+ { transferring [ `${ nft . contractAddress } _${ nft . id } ` ] ? 'Transferring...' : 'Transfer' }
214+ </ button >
215+ </ div >
98216 </ div >
99217 </ div >
100218 </ div >
0 commit comments