10 各位大大,EIP712验证不过去,求指导

我想验证下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

请先 登录 后评论

最佳答案 2024-09-03 17:36

  1. 确保 domainSeparator 在合约和 JavaScript 中计算方式一致。
  2. 确保 digest 的计算方式一致,尤其是 abi.encodePacked 和 abi.encode 的使用。
  3. 确保签名数据和结构一致,避免任何可能的偏差

expect(result[1] == holder).to.be.true; // 检查恢复的地址是否匹配

请先 登录 后评论

其它 2 个回答

Tiny熊
  擅长:智能合约,以太坊
请先 登录 后评论
zitup
请先 登录 后评论
  • 3 关注
  • 0 收藏,1088 浏览
  • 提出于 2024-08-16 09:06