I am trying to get the public key of the sender from a signed transaction. I am getting the transaction information with the help of alchemy and quicknode. The response I get is a json object like this:

blockHash: "0xd83af604301c3bc7d2f45ed601fdaddef833d69460b071f94f4303c55d22f63d"
blockNumber: "0x18fc94f"
chainId: "0x13881"
from: "0xdb326d217c2452faebb40aceb75000f0bb986703"
gas: "0xae72"
gasPrice: "0x3633f6180"
hash: "0x2e306ed90c0d99cad38b7f9e3a9e5440a7410bc375a8d3e459a317043ef68529"
input: "0x214270aa00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028313635313637353531382e35373138323438373737373735322e3337323536333539333232323738000000000000000000000000000000000000000000000000"
maxFeePerGas: "0x3633f6180"
maxPriorityFeePerGas: "0x3633f6175"
nonce: "0x3d"
r: "0xedfd326f515925ebb0a80cde14054d01c8fe90492fe2eaa39fc4fc71890c7de1"
s: "0xa9646ffd5d473f50b5fc79400777e0a46cd51b3618f935971184e8e7138554e"
to: "0x36b44c32bc25ef133f3c262317d809d181884457"
transactionIndex: "0x8"
type: 2
v: "0x1"
value: "0x12fd1"

I am trying to accomplish this by using the following code:

const txData = {
          type: parseInt(last_tx.type),
////gasPrice has to be equal to maxFeePerGas. I have tried both orders with no success
          maxFeePerGas: last_tx.gasPrice,
          gasPrice: last_tx.gasPrice,
          gasLimit: last_tx.gasLimit,
          value: last_tx.value,
          nonce: last_tx.nonce,
          data: last_tx.data,
          chainId: parseInt(last_tx.chainId),
          to: last_tx.to // you might need to include this if it's a regular tx and not simply a contract deployment
        const signature = ethers.utils.joinSignature(expandedSig);
        const rsTx = await ethers.utils.resolveProperties(txData);
        const raw = ethers.utils.serializeTransaction(rsTx); // returns RLP encoded tx
        console.log("raw", raw);
        const msgHash = ethers.utils.keccak256( raw); // as specified by ECDSA
        console.log("hash generated: ",msgHash);
        const msgBytes = ethers.utils.arrayify(msgHash); // create binary hash
        const recoveredPubKey = ethers.utils.recoverPublicKey(msgBytes, signature);
        const recoveredAddress = ethers.utils.recoverAddress(msgBytes, signature);
        console.log("recoveredPubKey", recoveredPubKey);
        console.log("recoveredAddress", recoveredAddress);

However, the address I get is wrong, and when I do this for all the transactions from an address it is always getting different public keys and addresses which makes me think there is something really wrong in the way I am calculating the hash of the transaction to sign.

I also read that it is necessary to prepend "\x19Ethereum Signed Message:\n32" to the transaction hash, and then hash it again, but this didn't work either. The way I was accomplishing this was by replacing:

const msgHash = ethers.utils.keccak256( raw); // as specified by ECDSA


const msgHash = ethers.utils.keccak256("0x225c783139457468657265756d205369676e6564204d6573736167653a5c6e333222"+ ethers.utils.keccak256(raw).slice(2));

where "0x225c783139457468657265756d205369676e6564204d6573736167653a5c6e333222" is the byte like representation of "\x19Ethereum Signed Message:\n32".

None of this works, and I simply don't know what I am doing wrong.

  • Similar questions have been asked before.link
    – LEVI_104
    Commented Jul 13, 2023 at 4:55


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.