Skip to content

Signature Verification

There are important details to verifying smart contract wallet signatures. The smart contract itself cannot produce a signature. Instead, the contract has a function

function isValidSignature(bytes32 _hash, bytes memory _signature) returns (bytes4 magicValue);

which can be called to determine if signature should be considered valid (defined in EIP-1271).

In the case of Smart Wallet, a signature is considered valid if it was signed by a current signer (aka "owner") of the Smart Wallet. For example, a user can sign a message with their passkey, and when isValidSignature is called on their Smart Wallet, it would return that the signature is valid because their passkey is an owner.

There is also an additional complexity: users receive their Smart Wallet address immediately upon passkey registration, and are able to begin signing for their Smart Wallet, but their Smart Wallet is not deployed on a chain until the first transaction on that chain.

ERC-6492 describes

With the rising popularity of account abstraction, we often find that the best user experience for contract wallets is to defer contract deployment until the first user transaction, therefore not burdening the user with an additional deploy step before they can use their account. However, at the same time, many dApps expect signatures, not only for interactions, but also just for logging in.

So the challenge is, how do we verify signatures in a way that works for both deployed and undeployed Smart Wallets? ERC-6492 has a solution for this, which Smart Wallet has adopted. We won't go unto all the details here, read the ERC linked above, if you're looking for that. Below we cover the minimum work needed to support on and off chain signature validation for Smart Wallet.

Offchain

For purely offchain signature verification––such as "Sign in With Ethereum"––ensure you are using a ERC-6492-compliant signature verification library. We recommend Viem's verifyMessage (example) and verifyTypedData (example).

Onchain

For signatures that will be used onchain, such as with Permit2 or Seaport developers will need to inspect the signature offchain and remove unneeded ERC-6492 data, if it is present.

example.ts
import {parseERC6492Signature} from "./utils"
 
const signatureForDeployedSmartWallet = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001517cb5e6438c7a32ba9bbc6766c6705c322c4f51bf28037af8f932e30e11c92e3b6b0080d3c2626ef4c6b0415ab036b47ca75ab9905753d7d3519fe09a56c98b0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22507a435038462d5252384b387557513241574d7650677032554f776362725774564d6e656856336f4a6530222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000";
const signatureForUndeployedSmartWallet = "0x0000000000000000000000000ba5ed0c6aa8c49038f819e587e2633c4a9f428a0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000e43ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004093a5e054721e22912d422ba5fca7ac395246e7f466b72c078bb987b71c5bf8ddf92c381340e22a301b823549d04765cd155a9b52206044121881395ab82d72c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001946068e132450c0189690dc29c0eb20a504e6c5a02c42ebc647c83f4e4a2e61e786bea55ea4695b4b72ef88ea133647a07ef02f2e9c0bfd39457a7eb18b519bd0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f77b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2234446462577159355831784c6b77735133584e31754736754a526f5a3731354a4643397767643332413767222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73652c226f746865725f6b6579735f63616e5f62655f61646465645f68657265223a22646f206e6f7420636f6d7061726520636c69656e74446174614a534f4e20616761696e737420612074656d706c6174652e205365652068747470733a2f2f676f6f2e676c2f796162506578227d0000000000000000006492649264926492649264926492649264926492649264926492649264926492";
 
parseERC6492Signature(signatureForDeployedSmartWallet)
{
sigToValidate: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001517cb5e6438c7a32ba9bbc6766c6705c322c4f51bf28037af8f932e30e11c92e3b6b0080d3c2626ef4c6b0415ab036b47ca75ab9905753d7d3519fe09a56c98b0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22507a435038462d5252384b387557513241574d7650677032554f776362725774564d6e656856336f4a6530222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000
}
parseERC6492Signature(signatureForUndeployedSmartWallet)
{
sigToValidate: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001946068e132450c0189690dc29c0eb20a504e6c5a02c42ebc647c83f4e4a2e61e786bea55ea4695b4b72ef88ea133647a07ef02f2e9c0bfd39457a7eb18b519bd0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f77b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2234446462577159355831784c6b77735133584e31754736754a526f5a3731354a4643397767643332413767222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73652c226f746865725f6b6579735f63616e5f62655f61646465645f68657265223a22646f206e6f7420636f6d7061726520636c69656e74446174614a534f4e20616761696e737420612074656d706c6174652e205365652068747470733a2f2f676f6f2e676c2f796162506578227d000000000000000000
factory: 0x0BA5ED0c6AA8c49038F819E587E2633c4A9F428a
factoryCalldata: 0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004093a5e054721e22912d422ba5fca7ac395246e7f466b72c078bb987b71c5bf8ddf92c381340e22a301b823549d04765cd155a9b52206044121881395ab82d72c8
}
// use `ParseERC6492ReturnType.sigToValidate` for onchain validation.