Publication
Partagez vos connaissances.
Why does Solidity produce a different address from a private key than ether.js does?
I have this function in my solidity contract:
function requestRandomNFT(
address minter,
uint256 nonce,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s,
uint256 randomUint256
) external payable {
require(block.timestamp <= deadline, "Request has expired");
require(nonce == _nonces[minter].current(), "Nonce does not match expected value");
bytes32 structHash = keccak256(
abi.encode(
_REQUEST_RANDOM_NFT_TYPEHASH,
keccak256(
abi.encode(
minter,
nonce,
deadline
)
)
)
);
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
emit Verified(signer, nonce, minter, r, s, v, signer);
console.log("below this is signer");
console.log(signer);
console.log("below this is aithorizedAccount");
console.log(authorizedAccount);
require(signer == authorizedAccount, ERR_INVALID_SIGNER);
bytes32 requestId = keccak256(abi.encodePacked(signer, nonce));
_nonces[minter].increment();
emit RequestedRandom(requestId);
// Auto mint NFT
mintNFT(minter, requestId, randomUint256);
}
I used import "hardhat/console.sol";
to write logs to the console.
This is my minting code:
import { ethers } from 'ethers';
import { useAccount } from "wagmi";
import { useContractWrite, useWaitForTransaction, usePrepareContractWrite } from 'wagmi';
import RandomReachDebug5Local from "../app/contracts/RandomReachDebug5Local.json";
import { useState, useEffect, useCallback } from 'react';
import { parseGwei } from 'viem'
// Function to get a signature for the random NFT request
async function signRandomNFTRequest(account, nonce, deadline) {
const privateKey = "0x REST OF MY PRIVATE KEY";
const wallet = new ethers.Wallet(privateKey);
const domain = {
name: "RandomReachDebug5Local",
version: "1",
chainId: 31337, // chainId for local node (Ganache or Hardhat)
verifyingContract: "0x8464135c8F25Da09e49BC8782676a84730C318bC", // replace with your contract address
};
const types = {
RequestRandomNFT: [
{ name: 'minter', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
const value = {
minter: account,
nonce: nonce,
deadline: deadline,
};
const signature = await wallet._signTypedData(domain, types, value);
const sig = ethers.utils.splitSignature(signature);
console.log("Signature: ", sig);
return sig;
}
export function useMintNFT() {
const { address: connectedAddrs, provider } = useAccount();
const [contract, setContract] = useState(null);
const [sigData, setSigData] = useState(null);
const [args, setArgs] = useState([]);
const [fetchNonceError, setFetchNonceError] = useState(null);
const randomReachDebug5Address = "0x8464135c8F25Da09e49BC8782676a84730C318bC"; // replace with your contract address
// Instantiate the contract
const initializeContract = useCallback(async () => {
if (window.ethereum && randomReachDebug5Address) {
console.log("initContract is running");
// If you use MetaMask
// const provider = new ethers.providers.Web3Provider(window.ethereum);
// If you use a local node (e.g., Ganache)
const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
const balance = await provider.getBalance(connectedAddrs);
console.log("Balance: ", ethers.utils.formatEther(balance));
const contractInstance = new ethers.Contract(randomReachDebug5Address, RandomReachDebug5Local.abi, provider.getSigner());
console.log(`contract is set here: ${contractInstance}`);
if (!sigData) {
setContract(contractInstance);
try {
const nonce = await contractInstance.nonces(connectedAddrs);
console.log("Nonce fetched: ", nonce);
const deadline = Math.floor(Date.now() / 1000) + 120; // 2 minutes from now
const sig = await signRandomNFTRequest(connectedAddrs, nonce, deadline);
setSigData(sig);
const randomUint256 = ethers.utils.hexlify(ethers.BigNumber.from(ethers.utils.randomBytes(32))); // generating random uint256
const argsInUseEffect = [connectedAddrs, nonce.toString(), deadline, sig.v, sig.r, sig.s, randomUint256];
console.log("Args in use effect: ", argsInUseEffect);
setArgs(argsInUseEffect);
} catch (error) {
console.error("Error fetching nonce: ", error);
setFetchNonceError(error);
}
}
console.log(`sig is here: ${sigData}`);
console.log(`args is here: ${args}`);
}
}, [args, connectedAddrs, randomReachDebug5Address, setContract]);
useEffect(() => {
initializeContract();
}, [initializeContract]);
console.log(`args outside of use effect ${args}`);
const { config, prepareError, isPrepareError } = usePrepareContractWrite({
address: randomReachDebug5Address,
abi: RandomReachDebug5Local.abi,
functionName: 'requestRandomNFT',
args: args,
value: '10000000000000000', // This is 0.01 Ether represented in Wei
gas: 300000n, // Set your desired gas limit here
gasPrice: parseGwei('50'),
});
console.log(`this is config: ${config}`);
const { data, error, isError, write } = useContractWrite(config || {});
console.log(`this is the error ${error} and this is the error bool ${isError}`);
const { isLoading, isSuccess } = useWaitForTransaction({hash: data?.hash});
const mintNFT = () => {
if (sigData) {
console.log('Minting...');
write?.();
} else {
console.error('sigData is not defined.');
}
};
return {
mintNFT,
isLoading,
isSuccess,
isError,
error,
data,
write,
isPrepareError,
prepareError,
fetchNonceError
};
}
So here's the weird thing... When I ran the minting code within a next.js app, I got this in my console for the hardhat node:
console.log:
below this is signer
0x2d0b2215d4e00807a6982877a9762ef41541ecef
below this is aithorizedAccount
0x1ab26702a8068a247bd33a9555dfef791d2bd68d
But the private key I used gives me the address 0x1AB26702A8068a247BD33a9555dfEf791d2BD68D when I test it in javascript with ethers.js, but solidity gives me 0x2d0b2215d4e00807a6982877a9762ef41541ecef ???
I used this script with const privateKey = "0x REST OF MY PRIVATE KEY":
const ethers = require('ethers');
function getAddressFromPrivateKey(privateKey) {
const wallet = new ethers.Wallet(privateKey);
return wallet.address;
}
const privateKey = "0x REST OF MY PRIVATE KEY";
console.log(getAddressFromPrivateKey(privateKey)); // This should log your expected address
This code gives me 0x1AB26702A8068a247BD33a9555dfEf791d2BD68D, but my solidity code gives me 0x2d0b2215d4e00807a6982877a9762ef41541ecef. That doesn't make any sense. Why is this happening?
- Smart Contract
- Solidity
By looking at the code I don't see any issues. Can you share the value for `_REQUEST_RANDOM_NFT_TYPEHASH` ?
I was actually able to figure out the problem. I just answered it. Thanks for following up though!
Réponses
1The problem was 1 little naming inconsistency. It took me a really a long time to figure it out.
This is types
in my minting code:
const types = {
Request: [
{ name: 'minter', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
I didn't display _REQUEST_RANDOM_NFT_TYPEHASH
in the smart contract, but it looks like this:
bytes32 internal constant REQUEST_TYPEHASH = keccak256(bytes("RequestRandom(address minter,uint256 nonce,uint256 deadline)"));
Notice in _REQUEST_RANDOM_NFT_TYPEHASH, the first word is RequestRandom
, and in types
(within the minting code), the first word is Request
. That word needs to be the same. I made them both Request
and the addresses matched.
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).
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.
- 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