EIP712 类型化数据签名合约一种更安全、先进的签名方式

  • 木西
  • 发布于 14小时前
  • 阅读 17

前言本文主要实现EIP721类型化数据签名的智能合约的开发、测试、部署、交互,测试过程:涉及到前端通过ethers库和合约以及钱包的交互;EIP712类型化数据签名定义:一种以太坊改进提案,旨在提供一种更高级、更安全的类型化数据签名方法;背景与重要性链下签名,链上验证:EIP712

前言

本文主要实现EIP721类型化数据签名的智能合约的开发、测试、部署、交互,测试过程:涉及到前端通过ethers库和合约以及钱包的交互;

EIP712 类型化数据签名

定义:一种以太坊改进提案,旨在提供一种更高级、更安全的类型化数据签名方法;

背景与重要性

  • 链下签名,链上验证:EIP712将签名过程从链上转移至链下,节省Gas费。这种链下消息签名、链上验证的形式,可以省去多余的approve操作。
  • 提高安全性与可用性:当支持EIP712的Dapp请求签名时,钱包会展示签名消息的原始数据,用户可以在验证数据符合预期之后签名。这使得用户能够更直观地了解他们正在签名的数据内容,而不是面对一串无法观察的编码。

工作流程

  1. 定义签名结构体数据类型
  2. 定义结构体的hash
  3. 定义编码数据
  4. 定义Domain Separator
  5. 生成摘要hash
  6. 验证签名者

应用场景

  • 数字资产交易:验证交易的合法性和完整性。
  • DApps身份验证:确保用户身份的合法性。
  • 智能合约的部署和调用:验证调用者的身份和权限。

合约开发

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "hardhat/console.sol";
contract EIP712Storage is EIP712 {
    bytes32 public constant PERMIT_TYPEHASH =
    keccak256("Permit(address owner,uint256 value,uint256 nonce,uint256 deadline)");
    mapping(address => uint256) public nonces;
    uint256 private _value;
    constructor() EIP712("EIP712Storage", "1.0.0") {}
    function storeData(
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        require(block.timestamp <= deadline, "EIP712Storage: expired deadline");
        bytes32 structHash = keccak256(
            abi.encode(
                PERMIT_TYPEHASH,
                msg.sender,
                value,
                nonces[msg.sender]++,
                deadline
            ));
        bytes32 hash = _hashTypedDataV4(structHash);
        address signer = ECDSA.recover(hash, v, r, s);
        require(signer == msg.sender, "EIP712Storage: invalid signature");
        _value = value;
    }
    function retrieve() public view returns (uint256) {
        return _value;
    }
    function getDomainSeparator() public view returns (bytes32) {
        return _domainSeparatorV4();
    }
}
    # 部署指令
    # npx hardhat deploy

合约本地测试(hardhat)

说明:主要测试签名有效、无效、时间过期三种情况的测试

const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("EIP712Storage", function () {
    let EIP712Storage;
    let eip712Storage;
    let owner;
    let addr1;
    let addr2;
    beforeEach(async function () {
         await deployments.fixture(["EIP712Storage"]);
        [owner, addr1, addr2] = await ethers.getSigners();
        const EIP712StorageFactory = await deployments.get("EIP712Storage");
        eip712Storage = await ethers.getContractAt("EIP712Storage",EIP712StorageFactory.address);
        //和上面是一样的都是部署合约
        // const EIP712StorageFactory = await ethers.getContractFactory("EIP712Storage");
        // eip712Storage = await EIP712StorageFactory.deploy();
        // await eip712Storage.waitForDeployment();

    });
    describe("Storing with EIP712 signature", function () {
        it("有效签名存储", async function () {
            const chainId = (await ethers.provider.getNetwork()).chainId;
            // 构建域分隔符
            const domain = {
                name: "EIP712Storage",
                version: "1.0.0",
                chainId: chainId,
                verifyingContract: await eip712Storage.getAddress()
            };
            // 定义类型
            const types = {
                Permit: [
                    { name: "owner", type: "address" },
                    { name: "value", type: "uint256" },
                    { name: "nonce", type: "uint256" },
                    { name: "deadline", type: "uint256" }
                ]
            };
            const value = 42;
            const deadline = Math.floor(Date.now() / 1000) + 60 * 60; // 1 hour from now
            const nonce = await eip712Storage.nonces(owner.address);
            // 准备签名数据
            const message = {
                owner: owner.address,
                value: value,
                nonce: nonce,
                deadline: deadline
            };
            // 签名
            const signature = await owner.signTypedData(domain, types, message);
            const { v, r, s } = ethers.Signature.from(signature);
            // 存储数据
            await eip712Storage.storeData(value, deadline, v, r, s);
            // 验证存储的值
            expect(await eip712Storage.retrieve()).to.equal(value);
        });
        it("日期过期测试", async function () {
            const chainId = (await ethers.provider.getNetwork()).chainId;
            const domain = {
                name: "EIP712Storage",
                version: "1.0.0",
                chainId: chainId,
                verifyingContract: await eip712Storage.getAddress()
            };
            const types = {
                Permit: [
                    { name: "owner", type: "address" },
                    { name: "value", type: "uint256" },
                    { name: "nonce", type: "uint256" },
                    { name: "deadline", type: "uint256" }
                ]
            }; 
            const value = 42;
            const deadline = Math.floor(Date.now() / 1000) - 60; // 1 minute ago
            const nonce = await eip712Storage.nonces(owner.address);
            const message = {
                owner: owner.address,
                value: value,
                nonce: nonce,
                deadline: deadline
            };
            const signature = await owner.signTypedData(domain, types, message);
            const { v, r, s } = ethers.Signature.from(signature);
            await expect(
                eip712Storage.storeData(value, deadline, v, r, s)
            ).to.be.revertedWith("EIP712Storage: expired deadline");
        });
        it("签名无效", async function () {
            const chainId = (await ethers.provider.getNetwork()).chainId;
            const domain = {
                name: "EIP712Storage",
                version: "1.0.0",
                chainId: chainId,
                verifyingContract: await eip712Storage.getAddress()
            };
            const types = {
                Permit: [
                    { name: "owner", type: "address" },
                    { name: "value", type: "uint256" },
                    { name: "nonce", type: "uint256" },
                    { name: "deadline", type: "uint256" }
                ]
            };
            const value = 42;
            const deadline = Math.floor(Date.now() / 1000) + 60 * 60;
            const nonce = await eip712Storage.nonces(owner.address);
            const message = {
                owner: owner.address,
                value: value,
                nonce: nonce,
                deadline: deadline
            };

            // 使用不同的签名者
            const signature = await addr1.signTypedData(domain, types, message)
            const { v, r, s } = ethers.Signature.from(signature);
            await expect(

                eip712Storage.connect(owner).storeData(value, deadline, v, r, s)

            ).to.be.revertedWith("EIP712Storage: invalid signature");

        });

    });

});
# 测试指令
# npx hardhat test ./test/xxx.js

合约部署

    module.exports = async function ({getNamedAccounts,deployments}) {
        const firstAccount = (await getNamedAccounts()).firstAccount;
        const {deploy,log} = deployments;
        const EIP712Storage=await deploy("EIP712Storage",{
            contract: "EIP712Storage",
            from: firstAccount,
            args: [],
            log: true,
            // waitConfirmations: 1,
        })
        console.log("EIP712Storage合约地址",EIP712Storage.address)
      }
      module.exports.tags = ["all", "EIP712Storage"]
    # 部署指令
    # npx hardhat deploy

总结

以上就是EIP712 类型化数据签名合约的开发、测试、部署全部流程,主要包含了测试了签名有效,无效和时间过期三种情况。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。