|
1 | 1 | import { SiweMessage } from "siwe"; |
2 | 2 | import { createPublicClient, http } from 'viem'; |
| 3 | +import { ethers } from 'ethers'; |
3 | 4 |
|
4 | 5 | const alchemyKey = process.env.ALCHEMY_API_KEY; |
5 | 6 |
|
@@ -57,16 +58,31 @@ export async function verifySiweMessage(message: string, signature: string) { |
57 | 58 | // console.log('Chain ID:', chainId); |
58 | 59 | // console.log('Nonce:', nonce); |
59 | 60 |
|
| 61 | + // Fast path: try local ECDSA recovery first. For standard EOA signatures |
| 62 | + // this avoids any RPC call, so verification can't be broken by upstream |
| 63 | + // RPC failures, rate limits, or missing API keys. |
| 64 | + try { |
| 65 | + const recovered = ethers.verifyMessage(message, signature); |
| 66 | + if (recovered.toLowerCase() === address.toLowerCase()) { |
| 67 | + return { |
| 68 | + isValid: true, |
| 69 | + address, |
| 70 | + expirationTime, |
| 71 | + nonce, |
| 72 | + }; |
| 73 | + } |
| 74 | + } catch { |
| 75 | + // Not a standard 65-byte ECDSA signature (e.g. smart wallet) — fall |
| 76 | + // through to the on-chain verification path below. |
| 77 | + } |
| 78 | + |
60 | 79 | // Use Alchemy or publicnode instead of WalletConnect RPC |
61 | 80 | const rpcUrl = getRpcUrl(chainId); |
62 | | - // console.log('RPC URL:', rpcUrl); |
63 | 81 |
|
64 | 82 | const publicClient = createPublicClient({ |
65 | 83 | transport: http(rpcUrl) |
66 | 84 | }); |
67 | 85 |
|
68 | | - // console.log('Calling publicClient.verifyMessage...'); |
69 | | - |
70 | 86 | // Check if the address is a contract (smart wallet) by checking bytecode |
71 | 87 | const bytecode = await publicClient.getBytecode({ address: address as `0x${string}` }); |
72 | 88 | const isContract = bytecode && bytecode !== '0x'; |
|
0 commit comments