Solidity.

Publication

Partagez vos connaissances.

Christian O'Connor.
May 27, 2023
Questions et Réponses avec des Experts

Solidity and ethers.js Compute Different Addresses from the Same Signature

This script:

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}`);

Gives this output: The signer is: 0xeB7265f6625EaE66403a637c073E63ccf33b8Cdc

So why does the javascript calculate 0x1AB26702A8068a247BD33a9555dfEf791d2BD68D, while hardhat's solidity compiler calculates 0xeB7265f6625EaE66403a637c073E63ccf33b8Cdc?

  • Smart Contract
  • Solidity
2
1
Partager
Commentaires
.

Réponses

1
Sergey Ilin.
Jun 2 2023, 05:35

I see one mismatch. REQUEST_TYPEHASH uses the type name Request. Type name in JavaScript when defining type is RequestRandomNFT.

Also, you can try this alternative code for signing in 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
Meilleure réponse
Commentaires
.

Connaissez-vous la réponse ?

Veuillez vous connecter et la partager.

Solidity is an object-oriented, high-level language for implementing smart contracts. It is a curly-bracket language designed to target the Ethereum Virtual Machine (EVM).

38Publications47Réponses
Sui.X.Peera.

Gagne ta part de 1000 Sui

Gagne des points de réputation et obtiens des récompenses pour avoir aidé la communauté Sui à se développer.

Nous utilisons des cookies pour vous assurer la meilleure expérience sur notre site Web.
Plus d'infos