Post
Share your knowledge.
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
Answers
1I 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]);
Do you know the answer?
Please log in and share it.
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).
- My ERC721 contract successfully deploys, but I can't verify the contract's source code with hardhat21
- Solidity and ethers.js Compute Different Addresses from the Same Signature21
- can't understand what are the locations(uint256)22
- How to reverse keccak256 in solidity22
- Clarification on Gas Refunds and Comparison Between "require" and "revert" in Smart Contracts21