orama/invest-api/auth/verify_evm.go
anonpenguin23 655bd92178 Squashed 'website/' content from commit d19b985
git-subtree-dir: website
git-subtree-split: d19b98589ec5d235560a210b26195b653a65a808
2026-03-26 18:14:59 +02:00

53 lines
1.4 KiB
Go

package auth
import (
"encoding/hex"
"fmt"
"strings"
"github.com/ethereum/go-ethereum/crypto"
)
// VerifyEVM verifies an Ethereum personal_sign signature.
func VerifyEVM(wallet, message, signatureHex string) error {
// Remove 0x prefix if present
sigHex := strings.TrimPrefix(signatureHex, "0x")
sigBytes, err := hex.DecodeString(sigHex)
if err != nil {
return fmt.Errorf("failed to decode signature hex: %w", err)
}
if len(sigBytes) != 65 {
return fmt.Errorf("invalid signature length: got %d, want 65", len(sigBytes))
}
// Ethereum personal_sign prefix
prefixed := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message)
hash := crypto.Keccak256Hash([]byte(prefixed))
// Fix recovery ID: some wallets return v=27/28, need v=0/1
if sigBytes[64] >= 27 {
sigBytes[64] -= 27
}
pubKeyBytes, err := crypto.Ecrecover(hash.Bytes(), sigBytes)
if err != nil {
return fmt.Errorf("ecrecover failed: %w", err)
}
pubKey, err := crypto.UnmarshalPubkey(pubKeyBytes)
if err != nil {
return fmt.Errorf("failed to unmarshal public key: %w", err)
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
expectedAddr := strings.ToLower(strings.TrimPrefix(wallet, "0x"))
recoveredHex := strings.ToLower(recoveredAddr.Hex()[2:])
if recoveredHex != expectedAddr {
return fmt.Errorf("signature verification failed: recovered %s, expected %s", recoveredHex, expectedAddr)
}
return nil
}