@tuwaio/siwx-evm
@tuwaio/siwx-evm is the EVM Layer 2 (L2) package of SIWX (Sign-In With X), the authentication project of TUWA Stage 1 (“Core Auth & Primitives”, next to Orbit Utils). Built on @tuwaio/siwx-core, viem and @wagmi/core, it signs CAIP-122 messages with EVM wallets and verifies eip155 signatures: EIP-191 for EOA wallets and EIP-1271 for smart contract wallets. It does not use ethers.js or web3.js.
🏛️ Core Capabilities
- Signing:
createEvmSiwxSignerturns a wagmiConfigor a viemWalletClientinto the(message) => signaturefunction thatuseSiwxfrom@tuwaio/siwx-reactexpects. - EOA verification:
verifyEip191recovers the signer of apersonal_signsignature and compares it with the message address. It runs locally, without RPC calls. - Smart contract wallets:
verifyEip1271callsisValidSignatureon the account contract (for example a Safe) through a viemPublicClient. - Combined check:
verifyEvmSignaturetries EIP-191 first and falls back to EIP-1271 when apublicClientis passed;result.methodtells which one succeeded. - No throwing: every verifier parses the message, requires an
eip155chain, checks the format, expiration andnotBeforewithvalidateMessage, and returns{ success, data, error }.
💾 Installation
pnpm add @tuwaio/siwx-evm @tuwaio/siwx-core @wagmi/core viem[!IMPORTANT]
@tuwaio/siwx-core,viem(>=2) and@wagmi/core(>=3) are peer dependencies and must be installed alongside@tuwaio/siwx-evm.@wagmi/coreis needed even if you only sign with a viemWalletClient.
🚀 Usage
Signing with wagmi or viem
import { createEvmSiwxSigner } from '@tuwaio/siwx-evm';
import type { Config } from '@wagmi/core';
import type { WalletClient } from 'viem';
declare const wagmiConfig: Config;
declare const walletClient: WalletClient;
declare const message: string;
// wagmi: signs with the connected account.
const signer = createEvmSiwxSigner(wagmiConfig);
// viem: signs with the client account, or with an explicit account.
const viemSigner = createEvmSiwxSigner(walletClient, '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B');
// Opens the wallet prompt. Rejects with "[SIWX-EVM] Signing failed: …" (original error in `cause`).
const signature = await signer(message);Verifying a signature
import { verifyEvmSignature } from '@tuwaio/siwx-evm';
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
declare const message: string;
declare const signature: `0x${string}`;
// Needed only for smart contract wallets (EIP-1271). Use a client for the chain of the message.
const publicClient = createPublicClient({ chain: mainnet, transport: http() });
const result = await verifyEvmSignature(message, signature, { publicClient });
if (result.success) {
console.log(result.method, result.data?.address); // "eip191" or "eip1271", "eip155:1:0x…"
} else {
console.error(result.error);
}- Use
verifyEip191alone when you only accept EOA wallets, andverifyEip1271when you know the account is a contract. - The verifiers do not check the domain, URI, nonce or other policy rules, and do not compare the client chain with the message
chainId. On a server, use@tuwaio/siwx-server, which adds the policy and single-use nonces, or runvalidatePolicyfrom@tuwaio/siwx-coreyourself. skipExpiration: truedisables the expiration check. Keep it off in production.
🌐 External Services
The package contacts no hosts of its own. verifyEip1271, and verifyEvmSignature when it falls back to EIP-1271, send one eth_call to the RPC endpoint of the publicClient you pass (with viem’s http() and no URL, that is the default public RPC of the chain). Signing goes through the wallet of the wagmi Config or WalletClient.
📚 API Reference
Every export, with signatures and types generated from the source, is documented at siwx.docs.tuwa.io/packages/siwx-evm .
📄 License
Licensed under the Apache-2.0 License. See the LICENSE file for details.