@@ -108,46 +108,82 @@ export default function handler(
108
108
function verifyEthereumSignature ( message : string , signature : string , expectedAddress : string ) : boolean {
109
109
try {
110
110
const secp256k1 = require ( 'secp256k1' ) ;
111
- const { keccak256 } = require ( 'keccak' ) ;
112
-
113
- // Ethereum personal sign message format
114
- const prefix = '\x19Ethereum Signed Message:\n' ;
115
- const prefixedMessage = prefix + message . length + message ;
116
-
117
- // Hash the prefixed message
118
- const messageHash = keccak256 ( Buffer . from ( prefixedMessage , 'utf8' ) ) ;
119
-
120
- // Remove 0x prefix if present and convert to buffer
121
- const sigHex = signature . startsWith ( '0x' ) ? signature . slice ( 2 ) : signature ;
122
- const sigBuffer = Buffer . from ( sigHex , 'hex' ) ;
123
-
124
- if ( sigBuffer . length !== 65 ) {
125
- throw new Error ( 'Invalid signature length' ) ;
126
- }
127
-
128
- // Extract r, s, v from signature
129
- const r = sigBuffer . slice ( 0 , 32 ) ;
130
- const s = sigBuffer . slice ( 32 , 64 ) ;
131
- let v = sigBuffer [ 64 ] ;
132
-
133
- // Handle recovery id
134
- if ( v < 27 ) {
135
- v += 27 ;
111
+ const keccak = require ( 'keccak' ) ;
112
+
113
+ console . log ( 'Verifying Ethereum signature:' ) ;
114
+ console . log ( 'Message:' , JSON . stringify ( message ) ) ;
115
+ console . log ( 'Signature:' , signature ) ;
116
+ console . log ( 'Expected address:' , expectedAddress ) ;
117
+
118
+ // Try different message formats that MetaMask might use
119
+ const messageFormats = [
120
+ message , // Original message
121
+ message . replace ( / \\ n / g, '\n' ) , // Replace escaped newlines
122
+ Buffer . from ( message , 'utf8' ) . toString ( ) , // Ensure UTF-8 encoding
123
+ ] ;
124
+
125
+ for ( let i = 0 ; i < messageFormats . length ; i ++ ) {
126
+ const testMessage = messageFormats [ i ] ;
127
+ console . log ( `\nTrying message format ${ i + 1 } :` , JSON . stringify ( testMessage ) ) ;
128
+
129
+ // Ethereum personal sign message format
130
+ const prefix = '\x19Ethereum Signed Message:\n' ;
131
+ const messageBuffer = Buffer . from ( testMessage , 'utf8' ) ;
132
+ const prefixedMessage = prefix + messageBuffer . length + testMessage ;
133
+
134
+ console . log ( 'Prefixed message:' , JSON . stringify ( prefixedMessage ) ) ;
135
+ console . log ( 'Message buffer length:' , messageBuffer . length ) ;
136
+
137
+ // Hash the prefixed message
138
+ const messageHash = keccak ( 'keccak256' ) . update ( Buffer . from ( prefixedMessage , 'utf8' ) ) . digest ( ) ;
139
+ console . log ( 'Message hash:' , messageHash . toString ( 'hex' ) ) ;
140
+
141
+ // Remove 0x prefix if present and convert to buffer
142
+ const sigHex = signature . startsWith ( '0x' ) ? signature . slice ( 2 ) : signature ;
143
+ const sigBuffer = Buffer . from ( sigHex , 'hex' ) ;
144
+
145
+ if ( sigBuffer . length !== 65 ) {
146
+ continue ;
147
+ }
148
+
149
+ // Extract r, s, v from signature
150
+ const r = sigBuffer . slice ( 0 , 32 ) ;
151
+ const s = sigBuffer . slice ( 32 , 64 ) ;
152
+ let v = sigBuffer [ 64 ] ;
153
+
154
+ console . log ( 'Original v:' , v ) ;
155
+
156
+ // Try both recovery IDs
157
+ for ( const recoveryId of [ 0 , 1 ] ) {
158
+ try {
159
+ console . log ( `Trying recovery ID: ${ recoveryId } ` ) ;
160
+
161
+ // Combine r and s for secp256k1
162
+ const signature65 = new Uint8Array ( [ ...r , ...s ] ) ;
163
+
164
+ // Recover public key
165
+ const publicKey = secp256k1 . ecdsaRecover ( signature65 , recoveryId , new Uint8Array ( messageHash ) ) ;
166
+
167
+ // Convert public key to address
168
+ const publicKeyBuffer = Buffer . from ( publicKey . slice ( 1 ) ) ;
169
+ const publicKeyHash = keccak ( 'keccak256' ) . update ( publicKeyBuffer ) . digest ( ) ;
170
+ const address = '0x' + publicKeyHash . slice ( - 20 ) . toString ( 'hex' ) ;
171
+
172
+ console . log ( `Recovered address: ${ address } ` ) ;
173
+
174
+ // Compare with expected address (case insensitive)
175
+ if ( address . toLowerCase ( ) === expectedAddress . toLowerCase ( ) ) {
176
+ console . log ( '✅ Signature verification successful!' ) ;
177
+ return true ;
178
+ }
179
+ } catch ( e ) {
180
+ console . log ( `❌ Failed with recovery ID ${ recoveryId } :` , e ) ;
181
+ }
182
+ }
136
183
}
137
- const recoveryId = v - 27 ;
138
-
139
- // Combine r and s for secp256k1
140
- const signature65 = new Uint8Array ( [ ...r , ...s ] ) ;
141
184
142
- // Recover public key
143
- const publicKey = secp256k1 . ecdsaRecover ( signature65 , recoveryId , new Uint8Array ( messageHash ) ) ;
144
-
145
- // Convert public key to address
146
- const publicKeyHash = keccak256 ( publicKey . slice ( 1 ) ) ; // Remove the 0x04 prefix
147
- const address = '0x' + publicKeyHash . slice ( - 20 ) . toString ( 'hex' ) ;
148
-
149
- // Compare with expected address (case insensitive)
150
- return address . toLowerCase ( ) === expectedAddress . toLowerCase ( ) ;
185
+ console . log ( '❌ All message formats and recovery IDs failed' ) ;
186
+ return false ;
151
187
152
188
} catch ( error ) {
153
189
console . error ( 'Error verifying Ethereum signature:' , error ) ;
0 commit comments