Solidity.

Publication

Partagez vos connaissances.

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

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
1
1
Partager
Commentaires
.
Sergey Ilin.
Jun 2 2023, 04:50

By looking at the code I don't see any issues. Can you share the value for `_REQUEST_RANDOM_NFT_TYPEHASH` ?

Christian O'Connor.
Jun 2 2023, 05:32

I was actually able to figure out the problem. I just answered it. Thanks for following up though!

Réponses

1
Christian O'Connor.
Jun 2 2023, 05:28

The 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.

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