Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Sign in with Ethereum
This crate provides a pure Rust implementation of EIP-4361: Sign In With Ethereum.
Installation
= "0.8"
Features
| Feature | Description |
|---|---|
serde |
Serialization/deserialization support |
alloy |
EIP-1271 contract wallet and EIP-6492 counterfactual wallet signature verification |
typed-builder |
Builder pattern for VerificationOpts |
Usage
Parsing a SIWE Message
Parsing is done via the Message implementation of FromStr:
# use Message;
let msg = "example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z";
let message: Message = msg.parse.unwrap;
The parser validates:
- EIP-55 checksummed address (unchecksummed all-lowercase/all-uppercase addresses are accepted with a warning in
message.warnings) - Alphanumeric nonce (minimum 8 characters)
- RFC 3339 timestamps
- RFC 3986 URI and domain
- Optional
scheme://prefix per EIP-4361 - Printable ASCII statement (no control characters)
Verifying a SIWE Message
Verification and authentication is performed via EIP-191, using the address field of the Message as the expected signer. This returns the Ethereum public key of the signer:
# use Message;
# use FromHex;
# let msg = "localhost:4361 wants you to sign in with your Ethereum account:\n0x6Da01670d8fc844e736095918bbE11fE8D564163\n\nSIWE Notepad Example\n\nURI: http://localhost:4361\nVersion: 1\nChain ID: 1\nNonce: kEWepMt9knR6lWJ6A\nIssued At: 2021-12-07T18:28:18.807Z";
# let message: Message = msg.parse.unwrap;
# let signature = from_hex.unwrap;
let signer: = message.verify_eip191.unwrap;
Time constraints (expiration and not-before) can be validated at current or specific times:
# use Message;
# use OffsetDateTime;
# let msg = "example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z";
# let message: Message = msg.parse.unwrap;
assert!;
// equivalent to
assert!;
Combined verification of time constraints, field bindings, and authentication can be done in a single call with verify:
# use FromHex;
# use ;
# use ;
#
# async
Serialization
Message instances serialize as their EIP-4361 string representation via the Display trait:
# use Message;
# let msg = "example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z";
# let message: Message = msg.parse.unwrap;
let formatted = message.to_string;
assert!;
EIP-191 Personal-Signature pre-hash signing input:
# use Message;
# let msg = "example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z";
# let message: Message = msg.parse.unwrap;
let eip191_bytes: = message.eip191_bytes.unwrap;
EIP-191 Personal-Signature hash (Keccak-256 of the above):
# use Message;
# let msg = "example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n\nURI: https://example.com\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z";
# let message: Message = msg.parse.unwrap;
let eip191_hash: = message.eip191_hash.unwrap;
Smart Contract Wallets (EIP-1271 / EIP-6492)
With the alloy feature enabled, verify() supports:
- EIP-1271 -- signature verification for deployed contract wallets (e.g. Safe, Argent)
- EIP-6492 -- signature verification for counterfactual (not yet deployed) contract wallets
Provide an RPC URL in the verification options. The verification order follows the EIP-6492 specification:
- EIP-6492 -- if the signature has the magic suffix, verify via the universal off-chain validator
- EOA -- try standard
ecrecoverfor 65-byte signatures - EIP-1271 -- fall back to on-chain
isValidSignatureif EOA verification fails
Example
use FromHex;
use ;
use FromStr;
use ;
async
Testing
Tests that require on-chain verification (EIP-1271 / EIP-6492) are marked #[ignore] and must be run explicitly with the alloy feature and an Ethereum mainnet RPC URL:
ETH_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
Migrating from siwe
This crate is the actively maintained successor to the siwe crate (v0.6), which is no longer maintained.
Cargo.toml
- siwe = "0.6"
+ signinwithethereum = "0.8"
Code changes
Rename the import:
- use siwe::{Message, VerificationOpts};
+ use signinwithethereum::{Message, VerificationOpts};
If you used the ethers feature for EIP-1271 contract wallet verification, switch to alloy:
- siwe = { version = "0.6", features = ["ethers"] }
+ signinwithethereum = { version = "0.8", features = ["alloy"] }
And replace the provider in VerificationOpts:
let opts = VerificationOpts {
- rpc_provider: Some("https://eth.llamarpc.com".try_into().unwrap()),
+ rpc_url: Some("https://eth.llamarpc.com".into()),
..Default::default()
};
The Message struct now has scheme: Option<String>, address_raw: Option<String>, and warnings: Vec<String> fields. If you construct Message values directly (rather than parsing), add them:
let msg = Message {
+ scheme: None,
domain: "example.com".parse().unwrap(),
address: [/* 20 bytes */],
+ address_raw: None,
// ...
+ warnings: vec![],
};
See CHANGELOG.md for the full list of breaking changes.