I did a thing(Polygon integration from t-ecdsa)

Maybe everyone has already tried this out, but I finally got the damn thing to work so I thought I’d share

https://twitter.com/afat/status/1562285531316293632

14 Likes

Beautiful! But now the mantra “not your keys , not your crypto” takes a spin along “alien math”.

1 Like

Love it! Also that you did this as part of ETH Mexico. We, as a community, should be more present on these events and show how the IC can also be a building block for blockchain orchestration.

6 Likes

I’ve been slammed…so a couple of observations, lessons learned, and questions.

  1. The public key you get is a compressed public key. If you want to get the eth address you need to keccak256 the uncompressed public key. I used ethers.utils.computeAddress(key); in ethers.js to finally get the right address.
  2. The signature returns a different signature everytime you ask for it even if the data is the same. I didn’t expect that and thought I was doing something wrong. I guess each possible transaction has a large number of signatures. Maybe I was still doing something wrong because some of these signatures would not ecrecover properly. It eventually worked.
  3. You need to set your v in your unsigned transaction if you are talking to polygon…this makes it use the chain id in the unsigned transaction.(web3.js). Otherwise you get two different hashes.

You’ll save 36 hours if you read the above. Feel free to ask questions.

let chainv = 0 + 80001 * 2 + 35;
const customChainParams = {name:Common.CustomChain.PolygonMumbai,
chainId: 80001,
networkId: 80001}


const txData = {
  from: "0x3b9bce081c17aa52b2e06e8ed290d2e50e45b392".toLocaleLowerCase(),
  nonce: Number(transactionCount),
  gasPrice: Number(36506920002),
  gasLimit: 120000,
  to: '0xd7bd4638b9eE43E24E3516E4AE13EC855c3DF2b5'.toLocaleLowerCase(),
  value: "0x0",
  data: data,
  chainId: 80001,
  v: Number(chainv)
  
}

const tx = Transaction.Transaction.fromTxData(txData, { customChainParams })
console.log("transaction", tx);

const hash = tx.getMessageToSign(true)//keccak256(Buffer.from(rawTransaction));

console.log("hash", hash.join(";"), hash.toString("hex"));

Now you have the hash to sign. You’ll get r and s from the canister and split it up in two

let r = "0x3cbd1047c05ceae02ee2d741499596b60589114c1760c6607442e7c709f95068";
let s = "0x3478863607e47e3182aa037b43e80c73e16591a62ef46a8d87df5021006fbb94";

const signedTransaction = Transaction.Transaction.fromTxData({
  from: "0x3b9bce081c17aa52b2e06e8ed290d2e50e45b392".toLocaleLowerCase(),
  nonce: Number(transactionCount),
  gasPrice: Number(36506920002),
  gasLimit: 120000,
  to: '0xd7bd4638b9eE43E24E3516E4AE13EC855c3DF2b5'.toLocaleLowerCase(),
  value: "0x0",
  data: data,
  chainId: 80001,
  r: r,
  s: s,
  v: Number(chainv)
}, { customChainParams });

let trx = await web3.eth.sendSignedTransaction("0x"+ toHexString(signedTransaction.serialize()));

console.log(trx);
7 Likes

Love your work, do you have github repo with complete code to look at?

It is so hacky it isn’t worth publishing. Between the above and the t-ecdsa example in the dfinity repo you should have all you need. When I get a publishable example together I’ll publish it. Feel free to ask any questions.

How did you come with value for chainv?

That is great news, that it is working :D. I’m currently working on next version of our NFT bridge from ETH, which I’m trying to do without any external validators. With the possibility to actualy sign and send transactions directly from RUST, IC gives you all the tools required for smooth integration :smiley:

Eip 155. Whether you add one or zero depends on if your r is even or odd…I’ve found that it is usually even.

1 Like

Nice work @skilesare!

The signature returns a different signature everytime you ask for it even if the data is the same. I didn’t expect that and thought I was doing something wrong. I guess each possible transaction has a large number of signatures.

This is expected, ecdsa is a randomized signature scheme, so you get a different one every time you sign.

Maybe I was still doing something wrong because some of these signatures would not ecrecover properly. It eventually worked.

Did you look at the frequency at which it succeeds / fails? If it’s 50%, I would suspect that something fails/succeeds depending on the parity bit of some number.

2 Likes