Pure-Go implementation of EIP-4361: Sign in with Ethereum.
This package is maintained by the Ethereum Identity Foundation alongside the canonical TypeScript, Python, and Rust libraries. All implementations share the same test vectors, ensuring byte-for-byte parity across languages.
go get github.com/signinwithethereum/siwe-gom, err := siwe.ParseMessage(messageStr)
if err != nil {
// parse error (structured *siwe.Error)
}Any non-fatal issues are surfaced via m.Warnings — for example, parsing
an address that is not EIP-55 checksummed emits a warning but still succeeds.
m, err := siwe.InitMessage(
"example.com", // domain
"0x71C7656EC7ab88b098defB751B7401B5f6d8976F", // address
"https://example.com", // uri
siwe.GenerateNonce(), // nonce
map[string]interface{}{
"statement": "Example statement for SIWE",
"chainId": 1,
"expirationTime": time.Now().Add(24 * time.Hour),
"requestId": "req-1234",
"resources": []string{"https://example.com/resources/1"},
},
)Serialize for signing with m.String(). EIP-191 pre-hash is available via
m.EIP191Hash() if you want to sign directly.
VerifyEIP191 returns the recovered public key for a valid EOA signature:
pub, err := m.VerifyEIP191(signatureHex)VerifyWith performs full verification including time, domain, nonce, URI,
chain-id, and request-id bindings, plus optional contract-wallet fallback.
It fails closed: Domain, Nonce, URI, and ChainID are all required and
must match the message exactly.
res, err := m.VerifyWith(ctx, signature, siwe.VerifyParams{
Domain: &domain,
Nonce: &nonce,
URI: &uri,
ChainID: &chainID,
}, siwe.VerifyOptions{})Wire any go-ethereum–compatible caller (*ethclient.Client implements the
EthCaller interface) into VerifyOptions.ContractVerifier:
import "github.com/ethereum/go-ethereum/ethclient"
cl, _ := ethclient.Dial("https://mainnet.infura.io/v3/...")
verifier := siwe.NewEthCallerVerifier(cl)
res, err := m.VerifyWith(ctx, signature, siwe.VerifyParams{
Domain: &domain,
Nonce: &nonce,
URI: &uri,
ChainID: &chainID,
}, siwe.VerifyOptions{
ContractVerifier: verifier,
})EOA recovery is attempted first; if it fails, the verifier is consulted via
isValidSignature(bytes32,bytes) per EIP-1271. Signatures carrying the
EIP-6492 magic suffix are handed to the universal off-chain validator
bytecode via eth_call, which covers counterfactual (undeployed) wallets
as well as already-deployed ones.
Note: this is verification only. EIP-6492 allows a verifier to optionally submit the factory transaction after a successful check to finalize on-chain deployment ("side-effectful" verification). This library does not do that — if you need the wallet actually deployed, submit the factory call yourself.
ok, err := m.ValidNow() // current time
ok, err = m.ValidAt(when) // specific pointhash := m.EIP191Hash()
sig, err := crypto.Sign(hash.Bytes(), privateKey)
if err != nil { /* ... */ }
sig[64] += 27 // normalize recovery byte for Ethereum wallets
hexSig := hexutil.Encode(sig)All errors returned by this package are *siwe.Error with a machine-readable
Type field corresponding to the SiweErrorType codes used by the canonical
TypeScript library. Match with errors.As:
var e *siwe.Error
if errors.As(err, &e) {
switch e.Type {
case siwe.ErrExpiredMessage:
case siwe.ErrNonceMismatch:
// ...
}
}Dual-licensed under MIT and Apache-2.0.