web3.js icon indicating copy to clipboard operation
web3.js copied to clipboard

The from address changes when the transaction gets signed and hashed.

Open zurikdev opened this issue 1 year ago • 5 comments

I need help with something. I am trying to create a transaction, sign it, and send the transaction with web3, but every time it gets signed, the from address changes to a random address.

After much evaluation and testing on my wallet without including the private key, I realized it only works when nonce is zero. But whenever it is a higher nonce such as 54, it generates a random from the wallet address.

So this method doesn't work on older wallet addresses.

import Transaction from "ethereumjs-tx";
import { toast } from "react-toastify";

const hashTx = (hash: any) => {
  const tx = new Transaction(hash);
  return tx;
};

  await web3.eth
    .getTransactionCount(target, "pending")
    .then(async (nonce1: any) => {
      const gasPrice: bigint = await web3.eth.getGasPrice();
      const gasLimit: bigint = 22000n;
      const txFee: bigint = BigInt(Math.floor(Number(gasPrice) * 1.666));
      const funds: bigint = BigInt(10000000000000000n - txFee * gasLimit);
      const chainId: bigint = await web3.eth.getChainId();
      const minEth: bigint = 1000000000000000n;

      if (getBalance > minEth && getBalance > txFee && host) {
        const tx_ = {
          from: myAddr,
          to: host,
          nonce: web3.utils.toHex(nonce1),
          gasLimit: web3.utils.toHex(21000n), // gasLimit
          gasPrice: web3.utils.toHex(txFee),
          value: web3.utils.toHex(funds),
          data: "0x",
          v: "0x1", // original
          r: "0x",
          s: "0x",
        };

        const tx = hashTx(tx_);
        const serializedTx = "0x" + tx.serialize().toString("hex");
        const sha3_: string = web3.utils.sha3(serializedTx)!;
        console.log("rawTx1:", serializedTx);
        console.log("rawHash1:", sha3_);

        await web3.eth
          .sign(sha3_, target)
          .then(async (signed: any) => {
            const temporary = signed.substring(2),
              r_ = "0x" + temporary.substring(0, 64),
              s_ = "0x" + temporary.substring(64, 128),
              rhema = parseInt(temporary.substring(128, 130), 16),
              v_ = web3.utils.toHex(rhema + Number(chainId) * 2 + 8);

            console.log("r:", r_);
            console.log("s:", s_);
            console.log("y:", v_);

            tx.r = r_;
            tx.s = s_;
            tx.v = v_;

            console.log(tx);
            console.log("---------------------------------------------");

            const txFin = "0x" + tx.serialize().toString("hex"); //,
            const sha3__ = web3.utils.sha3(txFin);

            console.log("rawTx:", txFin);
            console.log("rawHash:", sha3__);

            await web3.eth
              .sendSignedTransaction(txFin)
              .then((elisebeth: any) => {
                console.log(elisebeth);
                toast.success("Successful Attempt");
              })
              .catch((vannette: any) => {
                console.log(vannette)
              });
          })
          .catch((heide: any) => {
            toast.error("Failed To Process Request");
            console.log(heide);
          });
      } else {
        toast.error("Doesn't Meet Requirements.");
      }

After every hashing process, gives and insufficient balance prompt.

zurikdev avatar Jun 09 '24 00:06 zurikdev

Hi @zurikdev , which version of web3.js are you using? Here is a working example of what I think you are trying to do: https://docs.web3js.org/guides/wallet/transactions#sending-a-raw-transaction Pls let us know if this doesn't work and what error you are getting

SantiagoDevRel avatar Jun 11 '24 09:06 SantiagoDevRel

I'm currently using the web3.js v4.8.0.

zurikdev avatar Jun 11 '24 09:06 zurikdev

Hey there @zurikdev thanks for the issue, we'll take a look into this

luu-alex avatar Jun 14 '24 15:06 luu-alex

Your saying after web3.eth.sign the address is changed? What provider are you using as well?

luu-alex avatar Jun 14 '24 15:06 luu-alex

Your saying after web3.eth.sign the address is changed? What provider are you using as well?

Yes, after it got signed and rvs added to the transaction, the from address changes.

zurikdev avatar Jun 14 '24 21:06 zurikdev

@zurikdev I saw you are using web3.eth.sign(sha3_, target) for signing. target address in above call will be used for signing regardless what from you specify in your transaction in above scenario. so it depends on which provider you injected in web3 because in that injected wallet there must be account with target address and it must be unlocked. Based on these I created minimum code to sign and recover signer address and it works fine:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web3 Signing Example</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/web3.min.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        button {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
        }

        #result {
            margin-top: 20px;
            padding: 10px;
            border: 1px solid #ccc;
            background-color: #f9f9f9;
        }
    </style>
</head>

<body>
    <h1>Web3 Signing Example</h1>
    <button id="signButton">Sign Message</button>
    <div id="result"></div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const signButton = document.getElementById('signButton');
            const resultDiv = document.getElementById('result');

            let web3;

            async function initWeb3() {
                if (typeof window.ethereum !== 'undefined') {
                    try {
                        // Request account access
                        await window.ethereum.request({ method: 'eth_requestAccounts' });
                        web3 = new Web3(window.ethereum);
                        console.log('Web3 initialized with MetaMask');
                    } catch (error) {
                        console.error('User denied account access');
                    }
                } else {
                    console.log('MetaMask not detected');
                }
            }

            async function signMessage() {
                if (!web3) {
                    resultDiv.textContent = 'Web3 is not initialized. Please make sure MetaMask is installed and connected.';
                    return;
                }

                try {
                    const accounts = await web3.eth.getAccounts();
                    const message = "Hello, World!";

                    // Hash the message
                    const messageHash = web3.utils.sha3(message);

                    const addr = "0x....."; // account with this address must be present and unlocked in injected wallet
                    const signature = await web3.eth.sign(messageHash,  addr);

                    // Recover the signer address
                    const signerAddress = web3.eth.accounts.recover(messageHash, signature, true);

                    console.log('Signer address:', signerAddress);

                    
                    resultDiv.textContent = `Signature: ${signature} recovered: ${signerAddress} same:${addr==signerAddress}`;

                } catch (error) {
                    resultDiv.textContent = `Error: ${error.message}`;
                }
            }

            signButton.addEventListener('click', signMessage);

            // Initialize Web3 when the page loads
            initWeb3();
        });
    </script>
</body>

</html>

btw, you should be using personal sign or sign typed data instead of eth_sign

jdevcs avatar Jul 11 '24 15:07 jdevcs