<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      solidity學習之EIP712

      什么是EIP712

      EIP712是一種特殊的類型化數據簽名,與普通簽名不同,EIP712的簽名數據是結構化的。使用支持EIP712的Dapp進行簽名時,Dapp會展示簽名消息的結構化詳細數據,用戶可以對數據進行驗證,確認后再進行簽名。

      實現邏輯

      EIP712分為鏈下簽名和鏈上校驗兩部分,鏈下的簽名結構定義需要與鏈上的驗證合約保持一致。

      驗簽邏輯則和普通簽名相同,通過r,s,v驗證公鑰是否一致即可。

      具體實現

      鏈下簽名

      一個標準的簽名結構如下所示

      {
        "types": {
          "EIP712Domain": [
            { "name": "name", "type": "string" },
            { "name": "version", "type": "string" },
            { "name": "chainId", "type": "uint256" },
            { "name": "verifyingContract", "type": "address" }
          ],
          "Person": [
            { "name": "name", "type": "string" },
            { "name": "wallet", "type": "address" }
          ],
          "Mail": [
            { "name": "from", "type": "Person" },
            { "name": "to", "type": "Person" },
            { "name": "contents", "type": "string" }
          ]
        },
        "primaryType": "Mail",
        "domain": {
          "name": "MyDapp",
          "version": "1",
          "chainId": 1,
          "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
        },
        "message": {
          "from": {
            "name": "Alice",
            "wallet": "0x1234567890abcdef1234567890abcdef12345678"
          },
          "to": {
            "name": "Bob",
            "wallet": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
          },
          "contents": "Hello, Bob!"
        }
      }
      

      其中types是簽名信息中出現的數據結構的類型定義,包括固定的EIP712Domain以及自定義的結構體,在這個示例中是PersonMail。因為EIP712支持嵌套結構,可以看到Mail中出現了Person的成員變量。

      domainmessage就是types中定義的結構的具體實現,domain就是固定的指向EIP712Domain,其中nameversion需要與驗簽合約中定義的一致,chainIdverifyContract就是合約部署的鏈與地址。

      message在這個示例中就是一個Mail對象,可以看到primaryTypeMail,這代表了message的對象類型,因為支持嵌套,所以在解析時會從primaryType開始解析,然后逐步解析內置的其他結構體。在ether.js中會自動分析primaryType,所以無需指定。

      簽名時會按照結構體來向用戶展示message信息。

      鏈上合約

      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      
      contract EIP712Storage {
          using ECDSA for bytes32;
      
          bytes32 private constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
          bytes32 private constant STORAGE_TYPEHASH = keccak256("Person(string name,address wallet)");
          bytes32 private DOMAIN_SEPARATOR;
       }
      

      在變量中定義了兩個TYPEHASH常量,分別是domainmessage的type,用于后面生成簽名摘要。

          constructor(){
              DOMAIN_SEPARATOR = keccak256(abi.encode(
                  EIP712DOMAIN_TYPEHASH, // type hash
                  keccak256(bytes("EIP712Storage")), // name
                  keccak256(bytes("1")), // version
                  block.chainid, // chain id
                  address(this) // contract address
              ));
              owner = msg.sender;
          }
      

      在構造函數中定義了domainnameversion,因此鏈下簽名中的domain也要保持一致。

          function permitStore(string memory name, bytes memory _signature) public {
              // 檢查簽名長度,65是標準r,s,v簽名的長度
              require(_signature.length == 65, "invalid signature length");
              bytes32 r;
              bytes32 s;
              uint8 v;
              // 目前只能用assembly (內聯匯編)來從簽名中獲得r,s,v的值
              assembly {
                  /*
                  前32 bytes存儲簽名的長度 (動態數組存儲規則)
                  add(sig, 32) = sig的指針 + 32
                  等效為略過signature的前32 bytes
                  mload(p) 載入從內存地址p起始的接下來32 bytes數據
                  */
                  // 讀取長度數據后的32 bytes
                  r := mload(add(_signature, 0x20))
                  // 讀取之后的32 bytes
                  s := mload(add(_signature, 0x40))
                  // 讀取最后一個byte
                  v := byte(0, mload(add(_signature, 0x60)))
              }
      
              // 獲取簽名消息hash
              bytes32 digest = keccak256(abi.encodePacked(
                  "\x19\x01",
                  DOMAIN_SEPARATOR,
                  keccak256(abi.encode(STORAGE_TYPEHASH, name, msg.sender))
              )); 
              
              address signer = digest.recover(v, r, s); // 恢復簽名者
              require(signer == msg.sender, "EIP712Storage: Invalid signature"); // 檢查簽名
          }
      

      可以看到在方法中重新生成了一個簽名摘要digest,也就是message hash,其中 "\x19\x01"是簽名哈希的固定前綴,然后拼接上domainmessage內容的哈希,再調用recover恢復出signer

      此處的recover方法是openzeppelin庫的語法糖,因為前面通過 using ECDSA for bytes32;引入了ECDSA,用recover替代了底層的ecrecover實現,使校驗更方便。

      ERC20 Permit

      基于EIP712,可以在鏈下實現對ERC20token的授權,稱為ERC20Permit。

      即鏈下實現簽名,鏈上合約驗證,驗證成功后調用approve方法。在這種情況下token的owner無需持有gas,只需要在鏈下簽名后將簽名給到有gas的B,由B去執行,就可以實現將token授權給B甚至第三方的操作。

      // SPDX-License-Identifier: MIT
      
      pragma solidity ^0.8.0;
      
      import "./IERC20Permit.sol";
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
      
      /**
       * @dev ERC20 Permit 擴展的接口,允許通過簽名進行批準,如 https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]中定義。
       *
       * 添加了 {permit} 方法,可以通過帳戶簽名的消息更改帳戶的 ERC20 余額(參見 {IERC20-allowance})。通過不依賴 {IERC20-approve},代幣持有者的帳戶無需發送交易,因此完全不需要持有 Ether。
       */
      contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
          mapping(address => uint) private _nonces;
      
          bytes32 private constant _PERMIT_TYPEHASH =
              keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
      
          /**
           * @dev 初始化 EIP712 的 name 以及 ERC20 的 name 和 symbol
           */
          constructor(string memory name, string memory symbol) EIP712(name, "1") ERC20(name, symbol){}
      
          /**
           * @dev See {IERC20Permit-permit}.
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              // 檢查 deadline
              require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
      
              // 拼接 Hash
              bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
              bytes32 hash = _hashTypedDataV4(structHash);
              
              // 從簽名和消息計算 signer,并驗證簽名
              address signer = ECDSA.recover(hash, v, r, s);
              require(signer == owner, "ERC20Permit: invalid signature");
              
              // 授權
              _approve(owner, spender, value);
          }
      
          /**
           * @dev See {IERC20Permit-nonces}.
           */
          function nonces(address owner) public view virtual override returns (uint256) {
              return _nonces[owner];
          }
      
          /**
           * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
           */
          function DOMAIN_SEPARATOR() external view override returns (bytes32) {
              return _domainSeparatorV4();
          }
      
          /**
           * @dev "消費nonce": 返回 `owner` 當前的 `nonce`,并增加 1。
           */
          function _useNonce(address owner) internal virtual returns (uint256 current) {
              current = _nonces[owner];
              _nonces[owner] += 1;
          }
      }
      
      posted @ 2025-08-18 10:01  Felix07  閱讀(51)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 西西人体大胆444WWW| 激情综合网激情五月我去也| 国产精品久久人妻无码网站一区 | 亚洲中文字幕无码爆乳| 亚洲欧洲一区二区精品| 人人人澡人人肉久久精品| 人妻少妇88久久中文字幕| 国产亚洲AV电影院之毛片| 亚洲人成网站18禁止| 国产精品自拍一二三四区| 深夜精品免费在线观看| 南康市| 国产永久免费高清在线观看| 精品国产免费一区二区三区香蕉| 性视频一区| 天堂一区二区三区av| 99精品日本二区留学生| 又污又爽又黄的网站| 亚洲精品美女一区二区| 国产精品免费AⅤ片在线观看| 国产成人精品无码免费看| 亚洲午夜香蕉久久精品| 午夜成人无码免费看网站| 亚洲阿v天堂网2021| 久久天天躁夜夜躁一区| 少妇激情一区二区三区视频小说| 国产另类ts人妖一区二区| 免费A级毛片无码A∨蜜芽试看| 国内极度色诱视频网站| 无码国模国产在线观看免费| 宝贝腿开大点我添添公视频免| 日本道之久夂综合久久爱| 人妻系列中文字幕精品| 国产偷窥熟女高潮精品视频| 免费国产一区二区不卡| 日韩午夜一区二区福利视频| 国产无套精品一区二区| 人妻少妇精品性色av蜜桃| 在线日韩日本国产亚洲| 999精品全免费观看视频| 日韩激情成人|