diff --git a/docs/pages/guides/signature-verification.mdx b/docs/pages/guides/signature-verification.mdx new file mode 100644 index 0000000..e3b3938 --- /dev/null +++ b/docs/pages/guides/signature-verification.mdx @@ -0,0 +1,93 @@ +# Signature Verification + +The are important details to verifying smart contract wallet signatures. The smart contract itself +cannot produce a signature. Instead, the contract has a function +```solidity +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](https://eips.ethereum.org/EIPS/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](https://eips.ethereum.org/EIPS/eip-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, simply ensure you are using +a ERC-6492-compliant signature verification library, and it all should *just work*. +We recommend [Viem's `verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage). + +## Onchain +For signatures that will be used onchain, such as with [Permit2](https://github.com/Uniswap/permit2) +or [Seaport](https://github.com/ProjectOpenSea/seaport) developers will need to inspect the signature offchain +and remove unneeded ERC-6492 data, if it is present. + +:::code-group +```ts twoslash [example.ts] +import {parseERC6492Signature} from "./utils" + +const signatureForDeployedSmartWallet = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001517cb5e6438c7a32ba9bbc6766c6705c322c4f51bf28037af8f932e30e11c92e3b6b0080d3c2626ef4c6b0415ab036b47ca75ab9905753d7d3519fe09a56c98b0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22507a435038462d5252384b387557513241574d7650677032554f776362725774564d6e656856336f4a6530222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000"; +const signatureForUndeployedSmartWallet = "0x0000000000000000000000000ba5ed0c6aa8c49038f819e587e2633c4a9f428a0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000e43ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004093a5e054721e22912d422ba5fca7ac395246e7f466b72c078bb987b71c5bf8ddf92c381340e22a301b823549d04765cd155a9b52206044121881395ab82d72c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001946068e132450c0189690dc29c0eb20a504e6c5a02c42ebc647c83f4e4a2e61e786bea55ea4695b4b72ef88ea133647a07ef02f2e9c0bfd39457a7eb18b519bd0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f77b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2234446462577159355831784c6b77735133584e31754736754a526f5a3731354a4643397767643332413767222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73652c226f746865725f6b6579735f63616e5f62655f61646465645f68657265223a22646f206e6f7420636f6d7061726520636c69656e74446174614a534f4e20616761696e737420612074656d706c6174652e205365652068747470733a2f2f676f6f2e676c2f796162506578227d0000000000000000006492649264926492649264926492649264926492649264926492649264926492"; + +parseERC6492Signature(signatureForDeployedSmartWallet) +// @log: { +// @log: sigToValidate: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001517cb5e6438c7a32ba9bbc6766c6705c322c4f51bf28037af8f932e30e11c92e3b6b0080d3c2626ef4c6b0415ab036b47ca75ab9905753d7d3519fe09a56c98b0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22507a435038462d5252384b387557513241574d7650677032554f776362725774564d6e656856336f4a6530222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000 +// @log: } + +parseERC6492Signature(signatureForUndeployedSmartWallet) +// @log: { +// @log: sigToValidate: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001946068e132450c0189690dc29c0eb20a504e6c5a02c42ebc647c83f4e4a2e61e786bea55ea4695b4b72ef88ea133647a07ef02f2e9c0bfd39457a7eb18b519bd0000000000000000000000000000000000000000000000000000000000000025f198086b2db17256731bc456673b96bcef23f51d1fbacdd7c4379ef65465572f050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f77b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2234446462577159355831784c6b77735133584e31754736754a526f5a3731354a4643397767643332413767222c226f726967696e223a2268747470733a2f2f6b6579732e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73652c226f746865725f6b6579735f63616e5f62655f61646465645f68657265223a22646f206e6f7420636f6d7061726520636c69656e74446174614a534f4e20616761696e737420612074656d706c6174652e205365652068747470733a2f2f676f6f2e676c2f796162506578227d000000000000000000 +// @log: factory: 0x0BA5ED0c6AA8c49038F819E587E2633c4A9F428a +// @log: factoryCalldata: 0x3ffba36f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004093a5e054721e22912d422ba5fca7ac395246e7f466b72c078bb987b71c5bf8ddf92c381340e22a301b823549d04765cd155a9b52206044121881395ab82d72c8 +// @log: } + +// use `ParseERC6492ReturnType.sigToValidate` for onchain validation. + + +``` +```ts twoslash [utils.ts] filename="utils.ts" +import {type Hex, type Address, decodeAbiParameters} from 'viem' + +export function isERC6492Signature(signature: Hex) : boolean { + const ERC6492_DETECTION_SUFFIX = "6492649264926492649264926492649264926492649264926492649264926492"; + return signature.slice(signature.length - 64, signature.length) == ERC6492_DETECTION_SUFFIX; +} + +export type ParseERC6492ReturnType = { + sigToValidate: Hex; + factory?: Address; + factoryCalldata?: Hex; +}; + +// NOTE: Works with non-ERC-6492 siganture +// parseERC6492Signature(eoaSignature).sigToValidate == eoaSignature +// parseERC6492Signature(deployedSmartAccountSignature).sigToValidate == deployedSmartAccountSignature +export function parseERC6492Signature(signature: Hex): ParseERC6492ReturnType { + if (!isERC6492Signature(signature)) return { sigToValidate: signature }; + + const [factory, factoryCalldata, sigToValidate] = decodeAbiParameters( + [ + { type: "address" }, + { type: "bytes" }, + { type: "bytes" }, + ], + signature, + ); + return { sigToValidate, factory, factoryCalldata }; +} +``` +::: \ No newline at end of file diff --git a/vocs.config.ts b/vocs.config.ts index 2a7c4f4..9768cc2 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -57,6 +57,10 @@ export default defineConfig({ text: 'Update an existing app', link: '/guides/update-existing-app' }, + { + text: "Signature Verification", + link: '/guides/signature-verification' + }, { text: 'Batch transactions', link: '/guides/batch-transactions',