Solidity.

Bài viết

Chia sẻ kiến thức của bạn.

Christian O'Connor.
May 27, 2023
Hỏi đáp Chuyên Gia

Solidity và ethers.js Tính toán các địa chỉ khác nhau từ cùng một chữ ký

Kịch bản này:

const { ethers } = require('ethers');

async function recoverSigner(address, nonce, deadline, v, r, s) {
    const domain = {
        name: "RandomReachDebug5Local",
        version: "1",
        chainId: 31337,
        verifyingContract: "0x8464135c8F25Da09e49BC8782676a84730C318bC",
    };
    const types = {
        RequestRandomNFT: [
            { name: 'minter', type: 'address' },
            { name: 'nonce', type: 'uint256' },
            { name: 'deadline', type: 'uint256' },
        ],
    };
    const value = {
        minter: address,
        nonce: nonce,
        deadline: deadline,
    };

    // Get the digest of the message
    const digest = ethers.utils._TypedDataEncoder.hash(domain, types, value);
    
    const sig = {
        r: r,
        s: s,
        v: v,
    };

    if (sig.v < 27) {
        sig.v += 27;
    }

    const signer = ethers.utils.recoverAddress(digest, sig);
    
    return signer;
}

const address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const nonce = "0";
const deadline = "1685162164";
const v = "28";
const r = "0xe0e4cc2f8b6bac3784f9feb3db4382291631d5f317d08a0b82305e5eb5ffc60a";
const s = "0x0416d521d12e26b8fc1f4bd566d5d67c5dffebe9564fe90db4f12c0a69b63d5e";

async function run() {
    try {
        const signer = await recoverSigner(address, nonce, deadline, v, r, s);
        console.log(`The signer is: ${signer}`);
    } catch (error) {
        console.error(`Error in recovering signer: ${error}`);
    }
}

run();

Produces this output: The signer is: 0x1AB26702A8068a247BD33a9555dfEf791d2BD68D

But this solidity contract:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";

contract GetAddressFromSig is ERC721, EIP712 {
    struct Request {
        address minter;
        uint256 nonce;
        uint256 deadline;
    }

    bytes32 public constant REQUEST_TYPEHASH = keccak256("Request(address minter,uint256 nonce,uint256 deadline)");

    // Initialize _DOMAIN_SEPARATOR directly with static values
    bytes32 private immutable _DOMAIN_SEPARATOR = keccak256(
        abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes("RandomReachDebug5Local")), // static name
            keccak256(bytes("1")), // static version
            31337, // static chainId
            0x8464135c8F25Da09e49BC8782676a84730C318bC // static verifyingContract
        )
    );

    constructor(string memory name, string memory symbol) ERC721(name, symbol) EIP712(name, "1") {}

    function domainSeparator() public view returns (bytes32) {
        return _DOMAIN_SEPARATOR;
    }

    function recoverSigner(Request memory request, uint8 v, bytes32 r, bytes32 s) public view returns (address) {
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                _DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        REQUEST_TYPEHASH,
                        request.minter,
                        request.nonce,
                        request.deadline
                    )
                )
            )
        );

        // ECDSA.recover returns the address that is associated with the public key 
        // that was used to sign the given data, in this case, the digest.
        return ECDSA.recover(digest, v, r, s);
    }
}

Deployed with this script in hardhat:

import { ethers } from "hardhat";

const hre = require("hardhat");
const dotenv = require("dotenv");
dotenv.config();

async function main() {
  const GetAddressFromSig = await hre.ethers.getContractFactory("GetAddressFromSig");
  const gasPrice = await GetAddressFromSig.signer.getGasPrice();
  console.log(`Current gas price: ${gasPrice}`);
  const estimatedGas = await GetAddressFromSig.signer.estimateGas(
    GetAddressFromSig.getDeployTransaction("RANDOM NFT 5", "RNFT5"),
  );
  console.log(`Estimated gas: ${estimatedGas}`);
  const deploymentPrice = gasPrice.mul(estimatedGas);
  const deployerBalance = await GetAddressFromSig.signer.getBalance();
  console.log(`Deployer balance:  ${ethers.utils.formatEther(deployerBalance)}`);
  console.log(`Deployment price:  ${ethers.utils.formatEther(deploymentPrice)}`);
  const getAddressFromSig = await GetAddressFromSig.deploy("RANDOM NFT 5", "RNFT5");

  await getAddressFromSig.deployed();

  console.log("GetAddressFromSig deployed to:", getAddressFromSig.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

And ran with these commands in the hardhat console:

const Token = await ethers.getContractFactory("GetAddressFromSig");
const token = await Token.attach("0x8464135c8F25Da09e49BC8782676a84730C318bC");
const request = { minter: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", nonce: ethers.BigNumber.from("0"), deadline: ethers.BigNumber.from("1685162164") };
const v = 28
const r = "0xe0e4cc2f8b6bac3784f9feb3db4382291631d5f317d08a0b82305e5eb5ffc60a";
const s = "0x0416d521d12e26b8fc1f4bd566d5d67c5dffebe9564fe90db4f12c0a69b63d5e";
const signer = await token.recoverSigner(request, v, r, s);
console.log(`The signer is: ${signer}`);

Cung cấp cho đầu ra này: người ký là: 0xEB7265F6625EAE66403A637C073E63CCF33B8CDC

Vậy tại sao javascript tính 0x1AB26702A8068A247BD33A9555DFEF791D2BD68D, trong khi trình biên dịch solidity của hardhat tính 0xEB7265F6625EAE66403A637C073E63CCF33B8CDC?

  • Smart Contract
  • Solidity
2
1
Chia sẻ
Bình luận
.

Câu trả lời

1
Sergey Ilin.
Jun 2 2023, 05:35

Tôi thấy một sự không phù hợp. REQUEST_TYPEHASH uses the type name Request. Type name in JavaScript when defining type is RequestRandomNFT.

Ngoài ra, bạn có thể thử mã thay thế này để đăng nhập vào JavaScript:

   const value = {
        minter: address,
        nonce: nonce,
        deadline: deadline,
    };

    const domainType = [
      { name: 'name', type: 'string' },
      { name: 'version', type: 'string' },
      { name: 'chainId', type: 'uint256' },
      { name: 'verifyingContract', type: 'address' }
    ];

    const requestType = [
            { name: 'minter', type: 'address' },
            { name: 'nonce', type: 'uint256' },
            { name: 'deadline', type: 'uint256' },
    ];

    const domainData = {
      name: 'RandomReachDebug5Local',
      version: '1',
      chainId: 31337,
      verifyingContract: '0x8464135c8F25Da09e49BC8782676a84730C318bC',
      
    };

    const dataToSign = JSON.stringify({
      types: {
        EIP712Domain: domainType,
        Request: requestType,
      },
      domain: domainData,
      primaryType: 'Request',
      value,
    });

    const signature = await provider.send('eth_signTypedData_v4', [signerAddress, dataToSign]);
0
Câu trả lời hay nhất
Bình luận
.

Bạn có biết câu trả lời không?

Hãy đăng nhập và chia sẻ nó.

Chúng tôi sử dụng cookie để đảm bảo bạn có trải nghiệm tốt nhất trên trang web của chúng tôi.
Thêm thông tin