1
- import { useState , ChangeEvent } from 'react' ;
2
- import { Container , Button , Stack , Text , TextField , useTheme } from '@interchain-ui/react' ;
1
+ import { useState , useEffect } from 'react' ;
2
+ import { Container , Button , Stack , Text , useTheme } from '@interchain-ui/react' ;
3
3
import { useChain } from '@interchain-kit/react' ;
4
4
import { useChainStore } from '@/contexts' ;
5
5
import { useToast } from '@/hooks' ;
6
6
7
7
export default function SignMessage ( ) {
8
8
const [ message , setMessage ] = useState ( '' ) ;
9
- const [ signature , setSignature ] = useState ( '' ) ;
10
- const [ isValid , setIsValid ] = useState < boolean | null > ( null ) ;
11
- const [ verifying , setVerifying ] = useState ( false ) ;
9
+ const [ loading , setLoading ] = useState ( false ) ;
10
+ const [ signingIn , setSigningIn ] = useState ( false ) ;
12
11
const { selectedChain } = useChainStore ( ) ;
13
12
const { address, wallet, chain } = useChain ( selectedChain ) ;
14
13
const { toast } = useToast ( ) ;
15
14
const { theme } = useTheme ( ) ;
16
15
17
- const handleSign = async ( ) => {
18
- if ( ! wallet || ! address || ! chain . chainId ) {
16
+ useEffect ( ( ) => {
17
+ // Load the authentication message when component mounts
18
+ fetchAuthMessage ( ) ;
19
+ } , [ ] ) ;
20
+
21
+ const fetchAuthMessage = async ( ) => {
22
+ try {
23
+ setLoading ( true ) ;
24
+ const response = await fetch ( '/api/generate-auth-message' ) ;
25
+ const data = await response . json ( ) ;
26
+
27
+ if ( ! response . ok ) {
28
+ throw new Error ( data . error || 'Failed to fetch authentication message' ) ;
29
+ }
30
+
31
+ setMessage ( data . message ) ;
32
+ } catch ( error ) {
33
+ console . error ( 'Error fetching auth message:' , error ) ;
19
34
toast ( {
20
35
title : 'Error' ,
21
- description : 'Please connect your wallet first ' ,
36
+ description : 'Failed to fetch authentication message ' ,
22
37
type : 'error'
23
38
} ) ;
24
- return ;
39
+ } finally {
40
+ setLoading ( false ) ;
25
41
}
42
+ } ;
26
43
27
- try {
28
- setSignature ( '' ) ;
29
- setIsValid ( null ) ;
30
- const result = await wallet . signArbitrary ( chain . chainId , address , message ) ;
31
- setSignature ( result . signature ) ;
32
- } catch ( error ) {
33
- console . error ( 'Error signing message:' , error ) ;
44
+ const handleSignAndLogin = async ( ) => {
45
+ if ( ! wallet || ! address || ! chain . chainId ) {
34
46
toast ( {
35
47
title : 'Error' ,
36
- description : 'Failed to sign message: ' + ( error instanceof Error ? error . message : String ( error ) ) ,
48
+ description : 'Please connect your wallet first' ,
37
49
type : 'error'
38
50
} ) ;
51
+ return ;
39
52
}
40
- } ;
41
-
42
- const handleVerify = async ( ) => {
43
- if ( ! signature || ! address || ! chain . chainId ) return ;
44
53
45
54
try {
46
- setVerifying ( true ) ;
55
+ setSigningIn ( true ) ;
56
+
57
+ // Sign the message
58
+ const result = await wallet . signArbitrary ( chain . chainId , address , message ) ;
59
+
60
+ // Get the public key
47
61
const account = await wallet ?. getAccount ( chain . chainId ) ;
48
62
if ( ! account ?. pubkey ) {
49
63
throw new Error ( 'Failed to get public key' ) ;
50
64
}
51
65
66
+ // Submit to API directly
52
67
const response = await fetch ( '/api/verify-signature' , {
53
68
method : 'POST' ,
54
69
headers : {
55
70
'Content-Type' : 'application/json' ,
56
71
} ,
57
72
body : JSON . stringify ( {
58
73
message,
59
- signature,
74
+ signature : result . signature ,
60
75
publicKey : Buffer . from ( account . pubkey ) . toString ( 'base64' ) ,
61
76
signer : address
62
77
} ) ,
63
78
} ) ;
64
79
65
80
const data = await response . json ( ) ;
66
- setIsValid ( data . success ) ;
81
+
82
+ if ( ! response . ok ) {
83
+ throw new Error ( data . error || 'Login failed' ) ;
84
+ }
85
+
86
+ if ( ! data . success && data . message ?. includes ( 'expired' ) ) {
87
+ toast ( {
88
+ title : 'Authentication expired' ,
89
+ description : 'Authentication expired, please try again' ,
90
+ type : 'error'
91
+ } ) ;
92
+ handleRefreshMessage ( ) ;
93
+ return ;
94
+ }
95
+
96
+ if ( data . success ) {
97
+ toast ( {
98
+ title : 'Success' ,
99
+ description : 'Authentication successful' ,
100
+ type : 'success'
101
+ } ) ;
102
+ // Handle successful login - redirect or update UI state
103
+ // You can add navigation or state management here
104
+ } else {
105
+ throw new Error ( data . message || 'Authentication failed' ) ;
106
+ }
67
107
} catch ( error ) {
68
- console . error ( 'Error verifying signature :' , error ) ;
108
+ console . error ( 'Error signing in :' , error ) ;
69
109
toast ( {
70
110
title : 'Error' ,
71
- description : 'Failed to verify signature' ,
111
+ description : 'Failed to sign in: ' + ( error instanceof Error ? error . message : String ( error ) ) ,
72
112
type : 'error'
73
113
} ) ;
74
114
} finally {
75
- setVerifying ( false ) ;
115
+ setSigningIn ( false ) ;
76
116
}
77
117
} ;
78
118
79
- const handleMessageChange = ( e : ChangeEvent < HTMLInputElement > ) => {
80
- setMessage ( e . target . value ) ;
81
- setSignature ( '' ) ;
82
- setIsValid ( null ) ;
119
+ const handleRefreshMessage = ( ) => {
120
+ fetchAuthMessage ( ) ;
83
121
} ;
84
122
85
123
return (
@@ -93,64 +131,51 @@ export default function SignMessage() {
93
131
>
94
132
< Stack direction = "vertical" space = "$12" >
95
133
< Text as = "h1" fontSize = "$xl" fontWeight = "$semibold" >
96
- Sign Arbitrary Message
134
+ Sign Authentication Message
97
135
</ Text >
98
136
99
137
< Stack direction = "vertical" space = "$8" >
100
138
< Text as = "label" fontSize = "$md" >
101
- Message to Sign
139
+ Authentication Message
102
140
</ Text >
103
- < TextField
104
- id = "message"
105
- placeholder = "Enter your message here"
106
- value = { message }
107
- onChange = { handleMessageChange }
108
- />
141
+ < Container
142
+ attributes = { {
143
+ p : '$16' ,
144
+ backgroundColor : theme === 'light' ? '$gray100' : '$gray900' ,
145
+ borderRadius : '$md'
146
+ } }
147
+ >
148
+ { loading ? (
149
+ < Text > Loading authentication message...</ Text >
150
+ ) : (
151
+ < Text
152
+ fontSize = "$sm"
153
+ fontFamily = "$mono"
154
+ whiteSpace = "pre-wrap"
155
+ attributes = { { whiteSpace : 'pre-line' } } // This ensures line breaks are preserved
156
+ >
157
+ { message }
158
+ </ Text >
159
+ ) }
160
+ </ Container >
161
+ < Button
162
+ intent = "secondary"
163
+ onClick = { handleRefreshMessage }
164
+ size = "sm"
165
+ disabled = { loading || signingIn }
166
+ >
167
+ Refresh Message
168
+ </ Button >
109
169
</ Stack >
110
170
111
171
< Button
112
- intent = "tertiary"
113
- onClick = { handleSign }
114
- disabled = { ! message || ! wallet }
172
+ intent = "primary"
173
+ onClick = { handleSignAndLogin }
174
+ disabled = { ! message || ! wallet || loading }
175
+ isLoading = { signingIn }
115
176
>
116
- Sign Message
177
+ Sign and Login
117
178
</ Button >
118
-
119
- { signature && (
120
- < Stack direction = "vertical" space = "$8" >
121
- < Text fontWeight = "$semibold" > Signature:</ Text >
122
- < Container
123
- attributes = { {
124
- p : '$16' ,
125
- backgroundColor : theme === 'light' ? '$gray100' : '$gray900' ,
126
- borderRadius : '$md'
127
- } }
128
- >
129
- < Text fontSize = "$sm" fontFamily = "$mono" >
130
- { signature }
131
- </ Text >
132
- </ Container >
133
-
134
- < Button
135
- intent = "primary"
136
- onClick = { handleVerify }
137
- disabled = { verifying }
138
- isLoading = { verifying }
139
- >
140
- Verify Signature
141
- </ Button >
142
-
143
- { isValid !== null && (
144
- < Text
145
- fontSize = "$md"
146
- color = { isValid ? '$green500' : '$red500' }
147
- fontWeight = "$medium"
148
- >
149
- Signature is { isValid ? 'valid' : 'invalid' }
150
- </ Text >
151
- ) }
152
- </ Stack >
153
- ) }
154
179
</ Stack >
155
180
</ Container >
156
181
</ >
0 commit comments