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);
console.log("last_tx.raw",last_tx.raw);
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
for
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.