From a8b122dd85db91eecb4e70dcf986e2cd4456619c Mon Sep 17 00:00:00 2001 From: Benjamin Smith Date: Thu, 25 Apr 2024 13:08:50 +0200 Subject: [PATCH] Adapter.signMessage (#23) --- examples/sign-message.ts | 14 ++++++++++ package.json | 2 +- src/chains/ethereum.ts | 58 +++++++++++++++++++++++++++++++++++++++- tests/e2e.test.ts | 8 ++++-- yarn.lock | 15 ++++++++++- 5 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 examples/sign-message.ts diff --git a/examples/sign-message.ts b/examples/sign-message.ts new file mode 100644 index 0000000..601358d --- /dev/null +++ b/examples/sign-message.ts @@ -0,0 +1,14 @@ +import dotenv from "dotenv"; +import { setupNearEthAdapter } from "./setup"; +dotenv.config(); + +const run = async (): Promise => { + const evm = await setupNearEthAdapter(); + const message = "Hello World"; + console.log(`Signing "${message}" with ${evm.ethPublicKey()}`); + + const signature = await evm.signMessage(message); + console.log("Got Validated Signature", signature); +}; + +run(); diff --git a/package.json b/package.json index f02abef..bb5e999 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@typescript-eslint/parser": "^7.2.0", "dotenv": "^16.4.5", "eslint": "^8.57.0", - "ethers": "v6", + "ethers": "^6.12.0", "jest": "^29.7.0", "opensea-js": "^7.0.9", "prettier": "^3.2.5", diff --git a/src/chains/ethereum.ts b/src/chains/ethereum.ts index 13f2fc7..c84eb50 100644 --- a/src/chains/ethereum.ts +++ b/src/chains/ethereum.ts @@ -6,6 +6,12 @@ import { http, Hash, serializeTransaction, + hashMessage, + toBytes, + isBytes, + SignableMessage, + verifyMessage, + signatureToHex, } from "viem"; import { BaseTx, @@ -18,6 +24,7 @@ import { MultichainContract } from "../mpcContract"; import BN from "bn.js"; import { queryGasPrice } from "../utils/gasPrice"; import { buildTxPayload, addSignature } from "../utils/transaction"; +// import { ethers } from "ethers"; export class NearEthAdapter { ethClient: PublicClient; @@ -92,7 +99,7 @@ export class NearEthAdapter { signArgs, nearGas ); - + console.log("Signature received"); return this.relayTransaction({ transaction, signature: { big_r, big_s } }); } @@ -202,4 +209,53 @@ export class NearEthAdapter { console.log(`Transaction Confirmed: ${this.scanUrl}/tx/${txHash}`); return txHash; } + // Below code is inspired by https://github.com/Connor-ETHSeoul/near-viem + + async signMessage(message: SignableMessage): Promise { + const sigs = await this.sign(hashMessage(message)); + const address = this.ethPublicKey(); + const validity = await Promise.all([ + verifyMessage({ + address, + message, + signature: sigs[0], + }), + verifyMessage({ + address, + message: message, + signature: sigs[1], + }), + ]); + return this.pickValidSignature(validity, sigs); + } + + async sign(msgHash: `0x${string}` | Uint8Array): Promise<[Hex, Hex]> { + const hashToSign = isBytes(msgHash) ? msgHash : toBytes(msgHash); + + const { big_r, big_s } = await this.mpcContract.requestSignature({ + path: this.derivationPath, + payload: Array.from(hashToSign.reverse()), + key_version: 0, + }); + const r = `0x${big_r.substring(2)}` as Hex; + const s = `0x${big_s}` as Hex; + + return [ + signatureToHex({ r, s, yParity: 0 }), + signatureToHex({ r, s, yParity: 1 }), + ]; + } + + private pickValidSignature( + [valid0, valid1]: [boolean, boolean], + [sig0, sig1]: [Hash, Hash] + ): Hash { + if (!valid0 && !valid1) { + throw new Error("Invalid signature"); + } else if (valid0) { + return sig0; + } else { + return sig1; + } + } } diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index deb0ce1..f32e0e1 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -15,13 +15,13 @@ describe("End To End", () => { clearTimeout(); }); - it("Runs the Send ETH Tx", async () => { + it("signAndSendTransaction", async () => { await expect( evm.signAndSendTransaction({ to, value: ONE_WEI }) ).resolves.not.toThrow(); }); - it("Fails Invalid Send ETH Tx", async () => { + it("Fails to sign and send", async () => { const senderBalance = await getBalance(evm.ethClient, { address: evm.ethPublicKey(), }); @@ -29,4 +29,8 @@ describe("End To End", () => { evm.signAndSendTransaction({ to, value: senderBalance + ONE_WEI }) ).rejects.toThrow(); }); + + it("signMessage", async () => { + await expect(evm.signMessage("NearEth")).resolves.not.toThrow(); + }); }); diff --git a/yarn.lock b/yarn.lock index b305817..c9526e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1936,7 +1936,20 @@ ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: "@scure/bip32" "1.3.3" "@scure/bip39" "1.2.2" -ethers@^6.9.0, ethers@v6: +ethers@^6.12.0: + version "6.12.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.12.0.tgz#b0c2ce207ae5a3b5125be966e32ffea7c1f2481a" + integrity sha512-zL5NlOTjML239gIvtVJuaSk0N9GQLi1Hom3ZWUszE5lDTQE/IVB62mrPkQ2W1bGcZwVGSLaetQbWNQSvI4rGDQ== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "18.15.13" + aes-js "4.0.0-beta.5" + tslib "2.4.0" + ws "8.5.0" + +ethers@^6.9.0: version "6.11.1" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.11.1.tgz#96aae00b627c2e35f9b0a4d65c7ab658259ee6af" integrity sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==