I couldn't find a good explanation of each step of signTypedData
EIP with examples of inputs and outputs at each step. I wanted this so I could keep track.
Say this is our private key
const privKey = new Buffer(
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"hex"
);
Then we have the following public address:
const ethWallet = require("ethereumjs-wallet");
const wallet = ethWallet.fromPrivateKey(privKey);
const address = wallet.getAddressString();
console.log(address); // 0x627306090abab3a6e1400e9345bc60c78a8bef57
Say we want to sign the following data:
const params = [
{
type: "uint256",
name: "age",
value: 24
},
{
type: "string",
name: "name",
value: "John"
}
];
Using eth-sig-util
we can sign some typed data with this private key:
const ethSigUtil = require("eth-sig-util");
const signature = ethSigUtil.signTypedData(privKey, {
data: params
});
console.log(signature);
// 0x5dabb5919c805eb67fd8e651a8bb99b1a1b7359ad64b8719bc5a83281bf171ee1349a75c125b8534176df1e290d8510370df23459f18f609cb36963596fca0ad1b
What is going into this signature?
We are generating two hashes:
- Self describing schema including the names
- The actual values
In this case:
// 1. Self describing schema including the names
const schema = ["uint256 age", "string name"];
// 2. The actual values
const values = [24, "John"];
Each of these gets their own soliditySha3 digest:
const ethAbi = require("ethereumjs-abi");
const schemaHash = ethAbi.soliditySHA3(["string", "string"], schema);
const valuesHash = ethAbi.soliditySHA3(["uint256", "string"], values);
The values in this case being:
const ethUtil = require("ethereumjs-util");
console.log("schemaHash", ethUtil.bufferToHex(schemaHash));
console.log("valuesHash", ethUtil.bufferToHex(valuesHash));
// schemaHash 0x14efe1ecfe90efb39fdfe9c5234f86f12fa4b14af779d269a9f88e860f10207b
// valuesHash 0xaa7488f6657d5525a284ac67916e66434a8b74d178eb9882de911876a3e2f864
Finally, these output values are hashed together:
const signatureHash = ethAbi.soliditySHA3(
["bytes32", "bytes32"],
[schemaHash, valuesHash]
);
console.log("signatureHash", ethUtil.bufferToHex(signatureHash));
// signatureHash 0x884fd1fbda0e0d34ec6a0bcf5bb8293304efa29cee22f251bd0420a66cd546a7
With the signature hash calculated, it is a simple ecsign
now:
const sig = ethUtil.ecsign(signatureHash, privKey);
const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
console.log("Signature", rpcSig);
// Signature 0x5dabb5919c805eb67fd8e651a8bb99b1a1b7359ad64b8719bc5a83281bf171ee1349a75c125b8534176df1e290d8510370df23459f18f609cb36963596fca0ad00