11'use client' ;
22
3- import { useState } from 'react' ;
3+ import { useState , useEffect } from 'react' ;
44import { Button , Heading , Table , Link } from '@biom3/react' ;
55import NextLink from 'next/link' ;
66import { passportInstance } from '../utils/setupDefault' ;
77import { generateNonce } from 'siwe' ;
8- import { useConnect , useSignTypedData } from 'wagmi' ;
9- import { config } from '../utils/wagmiConfig' ;
10- import { metaMask } from 'wagmi/connectors' ;
8+ import { Provider } from '@imtbl/sdk/passport' ;
9+
10+ // Define a type for the window.ethereum object
11+ declare global {
12+ interface Window {
13+ ethereum ?: any ;
14+ }
15+ }
1116
1217export default function LinkExternalWallet ( ) {
1318 const [ isLoggedIn , setIsLoggedIn ] = useState < boolean > ( false ) ;
@@ -18,12 +23,25 @@ export default function LinkExternalWallet() {
1823 const [ linkingStatus , setLinkingStatus ] = useState < string > ( '' ) ;
1924 const [ linkingError , setLinkingError ] = useState < string | null > ( null ) ;
2025 const [ linkedAddresses , setLinkedAddresses ] = useState < string [ ] > ( [ ] ) ;
26+ const [ isMetaMaskInstalled , setIsMetaMaskInstalled ] = useState < boolean > ( false ) ;
27+
28+ // Check if MetaMask is installed
29+ useEffect ( ( ) => {
30+ if ( typeof window !== 'undefined' ) {
31+ setIsMetaMaskInstalled ( ! ! window . ethereum ) ;
32+ }
33+ } , [ ] ) ;
2134
22- const { connectAsync } = useConnect ( {
23- config
24- } ) ;
35+ // fetch the Passport provider from the Passport instance
36+ const [ passportProvider , setPassportProvider ] = useState < Provider > ( ) ;
2537
26- const { signTypedDataAsync } = useSignTypedData ( ) ;
38+ useEffect ( ( ) => {
39+ const fetchPassportProvider = async ( ) => {
40+ const passportProvider = await passportInstance . connectEvm ( ) ;
41+ setPassportProvider ( passportProvider ) ;
42+ } ;
43+ fetchPassportProvider ( ) ;
44+ } , [ ] ) ;
2745
2846 const loginWithPassport = async ( ) => {
2947 if ( ! passportInstance ) return ;
@@ -51,11 +69,20 @@ export default function LinkExternalWallet() {
5169 }
5270
5371 try {
54- // Use metaMask connector specifically instead of generic injected
55- const result = await connectAsync ( { connector : metaMask ( ) } ) ;
56- if ( result ?. accounts ?. [ 0 ] ) {
72+ if ( typeof window === 'undefined' || ! window . ethereum ) {
73+ setLinkingError ( 'MetaMask not installed. Please install MetaMask to continue.' ) ;
74+ return ;
75+ }
76+
77+ const accounts = await window . ethereum . request ( {
78+ method : 'eth_requestAccounts'
79+ } ) ;
80+
81+ if ( accounts && accounts . length > 0 ) {
5782 setWalletConnected ( true ) ;
58- setExternalWalletAddress ( result . accounts [ 0 ] ) ;
83+ setExternalWalletAddress ( accounts [ 0 ] ) ;
84+ } else {
85+ throw new Error ( 'No accounts found' ) ;
5986 }
6087 } catch ( error ) {
6188 console . error ( 'Error connecting to wallet:' , error ) ;
@@ -80,11 +107,7 @@ export default function LinkExternalWallet() {
80107 const formattedExternalWalletAddress = externalWalletAddress . toLowerCase ( ) as `0x${string } `;
81108 const formattedPassportAddress = accountAddress . toLowerCase ( ) as `0x${string } `;
82109
83- // Sign the message using EIP-712
84- const signature = await signTypedDataAsync ( {
85- domain : {
86- chainId : BigInt ( 1 )
87- } ,
110+ const dataToSign = {
88111 types : {
89112 EIP712Domain : [
90113 {
@@ -112,15 +135,43 @@ export default function LinkExternalWallet() {
112135 ]
113136 } ,
114137 primaryType : "LinkWallet" ,
138+ domain : {
139+ chainId : 13473 ,
140+ } ,
115141 message : {
116142 walletAddress : formattedExternalWalletAddress ,
117143 immutablePassportAddress : formattedPassportAddress ,
118144 condition : "I agree to link this wallet to my Immutable Passport account." ,
119145 nonce
120146 }
147+ }
148+
149+ if ( typeof window === 'undefined' || ! window . ethereum ) {
150+ throw new Error ( 'MetaMask not installed' ) ;
151+ }
152+
153+ // Sign the message using window.ethereum directly
154+ const signature = await window . ethereum . request ( {
155+ method : 'eth_signTypedData_v4' ,
156+ params : [ formattedExternalWalletAddress , JSON . stringify ( dataToSign ) ]
121157 } ) ;
122158
159+ // Import and use our signature validation function
160+ const { validateSignatureComprehensive } = await import ( '../utils/validateEIP712Signature' ) ;
161+
162+ const isValid = await validateSignatureComprehensive (
163+ formattedExternalWalletAddress , // the signer address
164+ dataToSign , // the payload
165+ signature ,
166+ window . ethereum // pass the provider for contract wallet validation if needed
167+ ) ;
168+
169+ if ( ! isValid ) {
170+ throw new Error ( 'Invalid signature' ) ;
171+ }
172+
123173 setLinkingStatus ( 'Linking wallet...' ) ;
174+
124175
125176 // Call the linkExternalWallet method to link the wallet
126177 const result = await passportInstance . linkExternalWallet ( {
@@ -129,6 +180,8 @@ export default function LinkExternalWallet() {
129180 signature,
130181 nonce
131182 } ) ;
183+
184+ console . log ( 'result' , result ) ;
132185
133186 const linkedAddresses = await passportInstance . getLinkedAddresses ( ) ;
134187 setLinkedAddresses ( linkedAddresses ) ;
@@ -147,15 +200,39 @@ export default function LinkExternalWallet() {
147200 < Heading size = "medium" className = "mb-1" >
148201 Link External Wallet
149202 </ Heading >
150- < Button
151- className = "mb-1"
152- size = "medium"
153- onClick = { loginWithPassport }
154- disabled = { isLoggedIn } >
155- { isLoggedIn ? 'Logged In' : 'Login' }
156- </ Button >
157-
158- < Table >
203+
204+ < div className = "mb-1" >
205+ { ! isLoggedIn ? (
206+ < Button
207+ size = "medium"
208+ onClick = { loginWithPassport } >
209+ Login with Passport
210+ </ Button >
211+ ) : (
212+ < >
213+ { ! walletConnected && (
214+ < Button
215+ size = "medium"
216+ onClick = { connectWallet }
217+ disabled = { walletConnected || ! isMetaMaskInstalled } >
218+ Connect Metamask
219+ </ Button >
220+ ) }
221+
222+ { walletConnected && (
223+ < Button
224+ size = "medium"
225+ onClick = { linkWallet }
226+ disabled = { isLinking || ! walletConnected } >
227+ { isLinking ? 'Linking...' : 'Link Wallet' }
228+ </ Button >
229+ ) }
230+ </ >
231+ ) }
232+ </ div >
233+
234+
235+ < Table className = "mb-1" >
159236 < Table . Head >
160237 < Table . Row >
161238 < Table . Cell > Attribute</ Table . Cell >
@@ -171,6 +248,10 @@ export default function LinkExternalWallet() {
171248 < Table . Cell > < b > Account Address</ b > </ Table . Cell >
172249 < Table . Cell > { accountAddress || 'N/A' } </ Table . Cell >
173250 </ Table . Row >
251+ < Table . Row >
252+ < Table . Cell > < b > MetaMask Available</ b > </ Table . Cell >
253+ < Table . Cell > { isMetaMaskInstalled ? 'Yes' : 'No' } </ Table . Cell >
254+ </ Table . Row >
174255 < Table . Row >
175256 < Table . Cell > < b > External Wallet</ b > </ Table . Cell >
176257 < Table . Cell > { externalWalletAddress || 'Not connected' } </ Table . Cell >
@@ -196,26 +277,7 @@ export default function LinkExternalWallet() {
196277 </ Table . Body >
197278 </ Table >
198279
199-
200- < Button
201- size = "medium"
202- onClick = { connectWallet }
203- disabled = { ! isLoggedIn || walletConnected } >
204- Connect Wallet
205- </ Button >
206-
207- { walletConnected && (
208- < Button
209- size = "medium"
210- onClick = { linkWallet }
211- className = "mt-6"
212- disabled = { isLinking || ! isLoggedIn || ! walletConnected } >
213- { isLinking ? 'Linking...' : 'Link Wallet' }
214- </ Button >
215- ) }
216-
217-
218- < div className = "mt-4" >
280+ < div className = "mt-1" >
219281 < Link rc = { < NextLink href = "/" /> } > Return to Examples</ Link >
220282 </ div >
221283 </ >
0 commit comments