Verifying Signatures with EIP-1271
Since contracts can’t generate signatures therefore ecrecover
won’t work if the address is a contract, then the contract-based account needs to use the EIP1271 standard to verify signatures for contracts.
Verifying contract-based account signatures require calling the EIP1271 isValidSignature( data, signature)
method on the account contract. Authereum, Dapper wallet, 0xProject, and a few others are already using EIP1271 to verify signatures where the "recovered address" is a contract address.
Below are examples of how to verify Authereum dapp key signatures:
Using Web3.js
Copy const Web3 = require ( 'web3' )
const provider = new Web3 . providers .HttpProvider ( 'https://kovan.infura.io' )
const web3 = new Web3 (provider)
const eip1271Abi = [
{
"constant" : true ,
"inputs" : [
{
"name" : "_messageHash" ,
"type" : "bytes"
} ,
{
"name" : "_signature" ,
"type" : "bytes"
}
] ,
"name" : "isValidSignature" ,
"outputs" : [
{
"name" : "magicValue" ,
"type" : "bytes4"
}
] ,
"payable" : false ,
"stateMutability" : "view" ,
"type" : "function"
}
]
const account = '0x99bd0006D13542A0917Cf8F2F986Ca7667b84268'
const data = '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad'
const signature = '0x0304494527023df3a811f5ad61aa35177a4455eb4bf098561f9380a574915f4c1ff4a5fc653afdfc086dcc9662848097703d18b82156618ccec1e5c9da7623e51b4760269d07f9a074dc2d6ab10cf52ff77852662e40fbb4b27289126a5bb538271e147c0952204161d710bb070a6e470b0b1ef65d11f1dc074e235e3dfaef00ae1b'
const magicValue = '0x20c13b0b'
const instance = await new web3 . eth .Contract (eip1271Abi , account)
const result = await instance . methods .isValidSignature (data , signature) .call ()
const verified = (result === magicValue)
console .log (verified) // true
The isValidSignature
method returns a magic which is computed from bytes4(keccak256("isValidSignature(bytes,bytes)").
The reason for returning a magic value to avoid accidentally returning true
.
Using ethers.js
Copy const ethers = require ( 'ethers' )
const provider = ethers .getDefaultProvider ( 'kovan' )
const eip1271Abi = [
{
"constant" : true ,
"inputs" : [
{
"name" : "_messageHash" ,
"type" : "bytes"
} ,
{
"name" : "_signature" ,
"type" : "bytes"
}
] ,
"name" : "isValidSignature" ,
"outputs" : [
{
"name" : "magicValue" ,
"type" : "bytes4"
}
] ,
"payable" : false ,
"stateMutability" : "view" ,
"type" : "function"
}
]
const account = '0x99bd0006D13542A0917Cf8F2F986Ca7667b84268'
const data = '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad'
const signature = '0x0304494527023df3a811f5ad61aa35177a4455eb4bf098561f9380a574915f4c1ff4a5fc653afdfc086dcc9662848097703d18b82156618ccec1e5c9da7623e51b4760269d07f9a074dc2d6ab10cf52ff77852662e40fbb4b27289126a5bb538271e147c0952204161d710bb070a6e470b0b1ef65d11f1dc074e235e3dfaef00ae1b'
const magicValue = '0x20c13b0b'
const instance = new ethers .Contract (account , eip1271Abi , provider)
const result = await instance .isValidSignature (data , signature)
const verified = (result === magicValue)
console .log (verified) // true
Using NPM package helper
For convenience, there's the is-valid-signature
NPM package you can use to validate signature for account contracts.
Copy const validateContractSignature = require ( 'is-valid-signature' )
const Web3 = require ( 'web3' )
const provider = new Web3 . providers .HttpProvider ( 'https://kovan.infura.io' )
const account = '0x99bd0006D13542A0917Cf8F2F986Ca7667b84268'
const data = '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad'
const signature = '0x0304494527023df3a811f5ad61aa35177a4455eb4bf098561f9380a574915f4c1ff4a5fc653afdfc086dcc9662848097703d18b82156618ccec1e5c9da7623e51b4760269d07f9a074dc2d6ab10cf52ff77852662e40fbb4b27289126a5bb538271e147c0952204161d710bb070a6e470b0b1ef65d11f1dc074e235e3dfaef00ae1b'
const verified = await validateContractSignature (account , data , signature , provider)
console .log (verified) // true
Resources