permit-712签名

permit-712签名

来源于EIP2612提案,先看文档再做操作
https://eips.ethereum.org/EIPS/eip-2612

合约代码

支持permit-712的代币,DAI,它的核心签名方法代码如下

function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                    bool allowed, uint8 v, bytes32 r, bytes32 s) external
    {
        bytes32 digest =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     allowed))
        ));

        require(holder != address(0), "Dai/invalid-address-0");
        require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
        require(expiry == 0 || now <= expiry, "Dai/permit-expired");
        require(nonce == nonces[holder]++, "Dai/invalid-nonce");
        uint wad = allowed ? uint(-1) : 0;
        allowance[holder][spender] = wad;
        emit Approval(holder, spender, wad);
    }

查看全部代码,
https://etherscan.io/address/0x6b175474e89094c44da98b954eedeac495271d0f#code

钱包签名实现

主要讲解web3实现
看metaMask钱包官方文档
https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4

web3签名实现

web3签名实现

步骤1、获取 PERMIT_TYPEHASH、DOMAIN_SEPARATOR

先看合约代码,发现PERMIT_TYPEHASH、DOMAIN_SEPARATOR定义都有public修饰符,且PERMIT_TYPEHASH有默认值,DOMAIN_SEPARATOR在初始化时已经赋值

 // --- EIP712 niceties ---
    bytes32 public DOMAIN_SEPARATOR;
    // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
    bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;

    constructor(uint256 chainId_) public {
        wards[msg.sender] = 1;
        DOMAIN_SEPARATOR = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes(name)),
            keccak256(bytes(version)),
            chainId_,
            address(this)
        ));
    }

获取方式1,通过rpc请求取合约数据,PERMIT_TYPEHASH、DOMAIN_SEPARATOR(建议)

const contract = new ClientContract(abi, '0x6b175474e89094c44da98b954eedeac495271d0f', 1)
    const calls = [
        contract.PERMIT_TYPEHASH(),
        contract.DOMAIN_SEPARATOR(),
    ]
    const [PERMIT_TYPEHASH, DOMAIN_SEPARATOR] = await multicallClient(calls)
	//DOMAIN_SEPARATOR: 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7
	//PERMIT_TYPEHASH: 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb

获取方式2,通过web3Utils进行计算(特殊情况可以使用此方式获取,顺便了解web3获取的方式)

const PERMIT_TYPEHASH = Web3.utils.keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)")
const web3 = new Web3(window.ethereum)
    const DOMAIN_SEPARATOR = Web3.utils.keccak256(
	// 第一个参数数组是合约里面参数的数据类型,第二个参数与合约代码里的参数对应
	web3.eth.abi.encodeParameters(['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], [
      Web3.utils.keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
      Web3.utils.keccak256('Dai Stablecoin'),//name
      Web3.utils.keccak256('1'),//version
      1,//chainId_
      '0x6B175474E89094C44Da98b954EedeAC495271d0F'//合约地址
    ]))
	//DOMAIN_SEPARATOR: 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7
	//PERMIT_TYPEHASH: 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb

对于合于名称,version,建议还是直接取合约数据(反正都要取合约数据,还是直接取计算好的hash更方便)

步骤2、获取structHash

先看合约代码获取部分


keccak256(abi.encode(PERMIT_TYPEHASH,holder,spender,nonce,expiry,allowed))
const digestHash = web3.utils.keccak256(web3.eth.abi.encodeParameters(['bytes32', 'address', 'address', 'uint256', 'uint256', 'bool'], [
          PERMIT_TYPEHASH,
          account,
          TWTRDao.address,
          nonce,
          deadline,
          buyTokenData.allowed]
        ))

web3代码

组合无限授权hash(如果你需要无限授权的话)

	// 获取nonce
    const tokenContract = new ClientContract(buyTokenData.abi, buyTokenData.address, ChainId.MAINNET)
   const nonce = await multicallClient([
      tokenContract.nonces(account)
    ]).then(data => data[0])
	

const digestHash = web3.utils.keccak256(web3.eth.abi.encodeParameters(['bytes32', 'address', 'address', 'uint256', 'uint256', 'bool'], [
          PERMIT_TYPEHASH,
          holder,//你的地址
          spender,//授权给目标地址
          nonce,//你在DAI里面的nonce
          expiry,//授权到期时间
          allowed,//填true表示无限授权
		  ]
        ))

组合指定量授权的hash(如果你需要有限授权的话)

	// 获取nonce
    const tokenContract = new ClientContract(buyTokenData.abi, buyTokenData.address, ChainId.MAINNET)
   const nonce = await multicallClient([
      tokenContract.nonces(account)
    ]).then(data => data[0])
	

const digestHash = web3.utils.keccak256(web3.eth.abi.encodeParameters(['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'], [
          PERMIT_TYPEHASH,
          holder,//你的地址
          spender,//授权给目标地址
		  amount,//你要授权的数量
          nonce,//你在DAI里面的nonce
          expiry,//授权到期时间
		  ]
        ))

步骤3、获取签名哈希 structHash

合约代码

bytes32 structHash =
            keccak256(abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(abi.encode(PERMIT_TYPEHASH,
                                     holder,
                                     spender,
                                     nonce,
                                     expiry,
                                     allowed))
        ));

web3代码

    const digest = web3.utils.soliditySha3(
      {
    
    
        type: 'string',
        value: '\x19\x01'
      },
      {
    
    
        type: 'bytes32',
        value: DOMAIN_SEPARATOR
      },
      {
    
    
        type: 'bytes32',
        value: structHash
      }
    )

步骤4、钱包签名

    const signatureHash = await web3.eth.sign(digest, account);
	// 0x150b79625e7321efe7ffa86f7141aa5906b185795647e27e2236c36bc407f0932010e8089ad6bb4f96ed0ec297cf8dc5553091b0a91eb5e8abc4958d27e3ac6c1c

此步骤会弹出钱包签名,用户同意签名之后得出一大串加密哈希,我们需要的是 v, r, s 所以要解析签名

解析签名

	import {
    
    fromRpcSig} from 'ethereumjs-util'
    const signature = fromRpcSig(signature)
	
	// 结果
	//{
    
    
    //   r: "0x150b79625e7321efe7ffa86f7141aa5906b185795647e27e2236c36bc407f093"
    //   s: "0x2010e8089ad6bb4f96ed0ec297cf8dc5553091b0a91eb5e8abc4958d27e3ac6c"
    //   v: 28
    // }

node签名

web3签名第四步替换为以下方法即可

//const {SigningKey} = require('@ethersproject/signing-key')
//signingKey.privateKey('0x........')
//const signature = SigningKey.signDigest(digest);


const SigningKey = new ethers.utils.SigningKey(私钥)
const signature = SigningKey.signDigest(digest);
// 结果
	//{
    
    
    //   r: "0x150b79625e7321efe7ffa86f7141aa5906b185795647e27e2236c36bc407f093"
    //   s: "0x2010e8089ad6bb4f96ed0ec297cf8dc5553091b0a91eb5e8abc4958d27e3ac6c"
    //   v: 28
    // }

猜你喜欢

转载自blog.csdn.net/weixin_43840202/article/details/122957126