@@ -108,46 +108,82 @@ export default function handler(
108108function verifyEthereumSignature ( message : string , signature : string , expectedAddress : string ) : boolean {
109109 try {
110110 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+ }
136183 }
137- const recoveryId = v - 27 ;
138-
139- // Combine r and s for secp256k1
140- const signature65 = new Uint8Array ( [ ...r , ...s ] ) ;
141184
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 ;
151187
152188 } catch ( error ) {
153189 console . error ( 'Error verifying Ethereum signature:' , error ) ;
0 commit comments