web3_signers 1.0.0
web3_signers: ^1.0.0 copied to clipboard
ERC-1271 and ERC-7739 compatible signature management for passkeys, local keys, and platform keys.
web3_signers #
A generic signing interface for Smart Accounts validation and EOAs.
This package provides a unified Signer interface to interact with various authentication credentials - Passkeys (WebAuthn), Platform Keys (Secure Enclave/TPM), and Local Private Keys. It allows developers to build Smart Account signers that are decoupled from specific wallet implementations, making it a foundational building block for any AA SDK or dApp.
Warning
Migrating from v0.x?
Significant breaking changes were introduced in v1.0.0. Please refer to the Migration Guide.
ABI Utilities #
The package includes fully viem-compatible ABI parsing and encoding utilities.
- Human-Readable Parsing: Parse
function,event,error, andtuplesignatures strings. - Flexible Encoding: Encode using signatures,
AbiParameterobjects, or raw JSON maps. - Deep Nesting: Full support for recursive tuples and arrays.
Features #
- 🔐 Passkeys (WebAuthn): Biometric and FIDO2 signing with configurable attestation and transports.
- 🛡️ Platform Keys: Hardware-backed keys using Secure Enclave (iOS/macOS), Keystore (Android), and Windows Hello.
- 🔑 Local Keys: Memory-based private key signing for ephemeral sessions or recovery.
- ⚡ Standard Compliant: Native support for EIP-1271 and ERC-7739 validations.
- 📱 Cross-Platform: Unified API for Android, iOS, macOS, Windows, and Web (partial).
Platform Requirements #
| Platform | Minimum Version | Notes |
|---|---|---|
| Android | Android 11 (API 30)+ | Required for modern biometric/keystore features. |
| iOS | iOS 13.0+ | Supports Secure Enclave and Authentication Services. |
| macOS | macOS 10.15+ (Catalina) | Supports Touch ID and platform authenticators. |
| Windows | Windows 10/11 | Requires CMake 3.14+ for build. |
Installation #
Add the package to your pubspec.yaml:
dependencies:
web3_signers: ^1.0.0
Usage #
1. Local Private Keys #
Best for: Development, testing, ephemeral session keys, or where users handle their own seed phrases.
import 'package:web3_signers/web3_signers.dart';
// Option A: Generate a random private key (for new wallets)
final privateKey = generatePrivateKey();
// Option B: Derive from a mnemonic
// 1. Generate a new mnemonic or use an existing one
final mnemonic = generateMnemonic(WordLength.word_12);
// 2. Derive the private key (supports optional custom path)
final privateKey = mnemonicToPrivateKey("your twelve word mnemonic ...", "m/44'/60'/0'/0/0");
// 3. Create the signer
// Direct from private key bytes:
final signer = LocalKeySigner.fromRawPrivateKey(privateKey);
// OR directly from a mnemonic string:
final signerFromMnemonic = LocalKeySigner.fromMnemonic("your twelve word mnemonic ...");
2. Passkeys (WebAuthn) #
Best for: Main account keys, biometric security, convenient cross-device access.
Step 1: Configuration: Define your Relying Party (RP) settings. This MUST match your domain.
final pkConfig = PassKeyConfig(
rpId: "app.example.com",
rpName: "Example App",
timeout: 60000,
userVerification: "required",
);
Step 2: Credential Creation (Registration): Prompt the user to create a new passkey.
// Returns the public key and credential metadata
final pkPublicKey = await generatePassKey(
config: pkConfig,
username: "user@example.com",
displayname: "User Name",
attestationLevel: PasskeyAttestationLevel.none,
// challenge is optional; a random one is generated if omitted
// auth is optional; a default is used if omitted
);
Step 3: Signer Instantiation
// Standard instantiation
final signer = PassKeySigner.withConfig(pkConfig, pkPublicKey);
// Advanced: Inject a custom authenticator instance (useful for dependency injection or reuse in key generation)
final auth = PasskeyAuthenticator();
final signer = PassKeySigner.withAuthenticator(auth, pkConfig, pkPublicKey);
3. Platform Keys (Secure Enclave / Keystore) #
Best for: Device-specific keys, high-security hardware backing without WebAuthn prompts.
Step 1: Configuration:
Platform keys offer granular control over security and UI via AndroidPlatformOptions, DarwinPlatformOptions, and WindowsPlatformOptions.
final platformConfig = PlatformConfig(
keyTag: "com.example.app.signing_key",
// Android: Prefer StrongBox, require authentication
androidOptions: AndroidPlatformOptions(
useStrongBoxKeyMint: true,
requireUserAuthentication: true,
authTimeoutSeconds: 0, // 0 means authenticate every time
// ... optional parameters
),
// iOS/macOS: Use Secure Enclave, strict access control
darwinOptions: DarwinPlatformOptions(
useSecureEnclave: true, // false uses keychain sharing (ensure entitlements are set)
accessible: DarwinAccessible.whenUnlockedThisDeviceOnly,
// ... other optional parameters
),
// Windows: Use TPM, custom prompt text
windowsOptions: WindowsPlatformOptions(
useTpm: true,
// ui policy is enforced by the presense of a `requireUserAuthentication` flag
requireUserAuthentication: true,
uiPolicyFriendlyName: "My App Wallet",
uiPolicyDescription: "Sign transactions for My App Wallet",
// ... other optional parameters
),
);
Step 2: Key Generation
final platformPublicKey = await generatePlatformKey(
config: platformConfig,
checkExisting: true, // Reuse existing key if present
// auth is optional; a default is used if omitted
);
Step 3: Signer Instantiation
// Standard instantiation
final signer = PlatformKeySigner.withConfig(platformConfig, platformPublicKey);
// To delete the key later (irreversible):
await signer.deleteSigningKey();
// Advanced: Inject a custom authenticator instance (useful for dependency injection or reuse in key generation)
final auth = PlatformAuthenticator();
final signer = PlatformKeySigner.withAuthenticator(
auth,
platformConfig,
platformPublicKey
);
Signer Interface #
All signers implement the Signer interface, providing a consistent way to interact with keys.
Properties #
// The signer's public key (PlatformPublicKey, PassKeyPublicKey, or LocalPublicKey)
final publicKey = signer.publicKey;
// The Ethereum address derived from the public key
final address = signer.getAddress();
// The type of signer (localKey, platformKey, passKey)
final type = signer.kind;
// Capabilities
final canSyncSign = signer.supportsSyncSigning; // True for LocalKeySigner
final supportsUserPresence = signer.supportsUserPresence; // True for Passkey/Platform
Signing Methods #
1. Async Signing (Preferred) Most compatible method, handles UI prompts for Passkeys/Platform keys.
final signature = await signer.signAsync(messageBytes);
2. Synchronous Signing
Only available if supportsSyncSigning is true (e.g., LocalKeySigner).
if (signer.supportsSyncSigning) {
try {
final signature = signer.sign(messageBytes);
} catch (e) {
// Handle error: Signer does not support sync signing
}
}
3. Personal Sign (EIP-191)
Prefixes the message with \x19Ethereum Signed Message:\n... before signing.
final signature = await signer.personalSign(utf8.encode("Hello Ethereum"));
4. Typed Data (EIP-712) Signs structured data.
final signature = await signer.signTypedData(
typedParams,
TypedDataVersion.V4
);
Signature Verification #
Use the Verifier class to validate signatures, including Smart Account (EIP-1271) signatures.
import 'package:web3_signers/web3_signers.dart';
// 1. Verify a Contract Signature (EIP-1271/7739)
final isValid = await Verifier.isValidContractSignature(
hash,
signatureBytes, // typed data bytes if using 7739
contractAddress,
"https://rpc.example.com",
);
if (isValid == IsValidSignatureResponse.success) {
print("Contract signature is valid!");
}
// 2. Verify an EOA/EC Signature
final isValidEC = Verifier.isValidECSignature(
originalPayload,
signature,
signerPublicKey
);
License #
This project is licensed under the BSD 3-Clause License.