solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1 <0.9.0;
/**
* EIP 712 V4
*/
contract SignEIP712 {
string public constant name = 'SignEIP712';
bytes32 public DOMAIN_SEPARATOR;
// keccak256("Main(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x1df57750cd6aa0d8a4d15ad41de6b3f43f7fe441023583db8e86d06c581788bc;
mapping(address => uint) public nonces;
address public testAddress; // just felix use custom test
constructor() {
uint chainId = block.chainid;
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1.0')),
chainId,
address(this)
)
);
}
function verify(
address owner,
address spender,
uint value,
uint deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(deadline >= block.timestamp, 'expired');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
testAddress = ecrecover(digest, v, r, s);
}
function getTestVerifyValue() view external returns(address){
return testAddress;
}
}
ts
import {ethers, run} from 'hardhat';
import "@nomiclabs/hardhat-ethers";
import {expect} from 'chai';
describe("SignEIP712.sol", () => {
let token: any;
before(async () => { await run("compile"); }); // before compile
beforeEach(async () => {
const Token = await ethers.getContractFactory("contracts/Sign/SignEIP712.sol:SignEIP712");
token = await Token.deploy();
await token.deployed();
});
describe("#permit", () => {
it("base", async () => {
const owner = await token.signer.getAddress();
const spender = await (await ethers.getSigners())[1].getAddress();
const myValue = ethers.BigNumber.from(10).mul(ethers.BigNumber.from(10).pow(18));
const nonce = ethers.BigNumber.from(await token.nonces(owner));
const deadline = ethers.constants.MaxUint256;
const td = {
domain:{
name: await token.name(),
version: '1.0',
chainId: await token.signer.getChainId(),
verifyingContract: token.address
},
types: {
Main: [
{name:'owner', type:'address'},
{name:'spender', type:'address'},
{name:'value', type:'uint256'},
{name:'nonce', type:'uint256'},
{name:'deadline', type:'uint256'},
]
},
message:{
owner:owner,
spender:spender,
value:myValue,
nonce:nonce,
deadline:deadline
}
}
const flatSig = await token.signer._signTypedData(td.domain, td.types, td.message);
const sig = ethers.utils.splitSignature(flatSig);
await token.verify(owner, spender, myValue, deadline, sig.v, sig.r, sig.s);
const result = await token.getTestVerifyValue();
expect(result.toLowerCase()).equal(owner.toLowerCase());
});
});
});
网友评论