我想验证下712的离线签名验证,所以我写了一个这样的合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* eip712 三要素:TYPEHASH、DOMAIN_SEPERATOR、digest
*/
contract PermitDemo {
bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,bool allowed)");
bytes32 private PERMIT_DOMAIN_SEPERATOR;
constructor() {
PERMIT_DOMAIN_SEPERATOR = keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("PermitDemo")),
keccak256(bytes("1")),
31337,
address(this)
));
}
function permit(
address holder,
address spender,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external view returns (address, address, bytes32) {
require(holder != address(0), "PermitDemo: invalid address 0");
bytes32 digest = keccak256(abi.encode(
keccak256("\x19\x01"),
PERMIT_DOMAIN_SEPERATOR,
keccak256(abi.encode(
PERMIT_TYPEHASH,
holder,
spender,
allowed
))
));
return (holder, ecrecover(digest, v, r, s), digest);
}
}
接着,我又写了一个这样的验证js:
// test/permit-demo.test.js
const { expect } = require('chai');
const { ethers } = require('hardhat');
describe('PermitDemo', function () {
let permitDemo;
let owner;
let otherAccount;
const domain = {
name: "PermitDemo",
version: "1",
chainId: 31337, // hardhat.config.js
verifyingContract: "0xYourContractAddress" // 替换为合约地址
};
const types = {
Permit: [
{ name: "holder", type: "address" },
{ name: "spender", type: "address" },
{ name: "allowed", type: "bool" }
]
};
before(async function () {
[owner, otherAccount] = await ethers.getSigners();
const PermitDemo = await ethers.getContractFactory('PermitDemo');
permitDemo = await PermitDemo.deploy();
await permitDemo.waitForDeployment();
domain.verifyingContract = await permitDemo.getAddress();
});
it('correctly verify permit', async function () {
const holder = owner.address;
const spender = otherAccount.address;
const allowed = true;
const domainSeparator = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(
['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'],
[
ethers.keccak256(ethers.toUtf8Bytes("PermitDemo(string name,string version,uint256 chainId,address verifyingContract)")),
ethers.keccak256(ethers.toUtf8Bytes("PermitDemo")),
ethers.keccak256(ethers.toUtf8Bytes("1")),
31337, // chainId
domain.verifyingContract // 合约地址
]
));
const PERMIT_TYPEHASH = ethers.keccak256(ethers.toUtf8Bytes("Permit(address holder,address spender,bool allowed)"));
const structHash = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(
['bytes32', 'address', 'address', 'bool'],
[PERMIT_TYPEHASH, holder, spender, allowed]
));
const digest = ethers.keccak256(ethers.concat([
ethers.toUtf8Bytes("\x19\x01"),
domainSeparator,
structHash
]));
const signature = await owner.signTypedData(domain, types, { holder, spender, allowed });
const recoveredAddress = ethers.verifyTypedData(domain, types, { holder, spender, allowed }, signature);
console.log("Recovered Address ===> ", recoveredAddress);
const { v, r, s } = ethers.Signature.from(signature);
const result = await permitDemo.permit(holder, spender, allowed, v, r, s);
console.log("result ====> ", result);
expect(result[0] == holder).to.be.true;
});
});
但是我得到的结果是这样的:
Recovered Address ===> 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
result ====> Result(3) [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'0x53CbeDAAcCC3f805C5672466Dd5F4b0bf72Ec0c0',
'0xae381fb0452782aa0e416e1619ccc6040ab666813ea8e0dc05881e2637faa55c'
]
也就是说在js中recover出来的地址是holder,但是在合约中得到的地址却是:0x53CbeDAAcCC3f805C5672466Dd5F4b0bf72Ec0c0