Signature Validation
Smart Wallets rely on EIP-1271 standard for signature validation.
The EIP-1271 is a single function on a contract defined as:
function isValidSignature(
bytes32 _hash,
bytes memory _signature
) public view returns (bytes4 magicValue)
The first _dataHash
argument accepts the hash of the message digest, and the second argument _signature
is the signed payload returned by the wallet upon signing.
Ethers v6 Example
First check if the user wallet is an EOA or a Smart Wallet:
import { Contract, JsonRpcProvider, Wallet, hashMessage } from "ethers";
const jsonRpcNodeProvider = process.env.JSON_RPC_NODE_PROVIDER;
const chainId = BigInt(process.env.CHAIN_ID as string);
const provider = new JsonRpcProvider(jsonRpcNodeProvider, chainId);
export async function isSmartContract(address: string, provider: any) {
try {
const code = await provider.getCode(address);
return code !== '0x';
} catch (error) {
console.error(error);
return false;
}
}
const isSmartContractWallet = isSmartContract(walletAddress);
Second, if true
, you can use the isValidSignature
method to validate the signature. This example validates a EIP-712 type data message
// Wallet returns the message you asked them to sign previously
const signature = "0x5966aaa07d494059d70b5a94bb5085721e8937033c1d73b6380786d55bc815a832bb14d11ddc406512a1a27ebcbd7b7c4f0ce686c654a20b8c060cd925a81ec51c";
const walletAddress = "0xb8741a449d50ed0dcfe395287f85be152884c8d9";
const abiSmartWallet = ['function isValidSignature(bytes32 _dataHash, bytes calldata _signature) external view returns (bytes4)'];
const userAccountContract = new Contract(walletAddress, abiSmartWallet, provider);
const hashMessage = hashMessage("Hello World");
const returnValue = await userAccountContract.isValidSignature(hashMessage, signature);
console.log(returnValue, "If success, it will return a magic value 0x1626ba7e");