From d2d635831667b9603ac8030b5d611822e38e8a17 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:21:30 -0800 Subject: [PATCH 01/51] Create frames validator --- packages/frames-validator/package.json | 39 +++++++ packages/frames-validator/src/crypto.ts | 6 ++ packages/frames-validator/src/index.test.ts | 113 ++++++++++++++++++++ packages/frames-validator/src/index.ts | 97 +++++++++++++++++ packages/frames-validator/src/types.ts | 17 +++ packages/frames-validator/tsconfig.json | 7 ++ packages/frames-validator/vitest.config.ts | 17 +++ packages/frames-validator/vitest.setup.ts | 5 + 8 files changed, 301 insertions(+) create mode 100644 packages/frames-validator/package.json create mode 100644 packages/frames-validator/src/crypto.ts create mode 100644 packages/frames-validator/src/index.test.ts create mode 100644 packages/frames-validator/src/index.ts create mode 100644 packages/frames-validator/src/types.ts create mode 100644 packages/frames-validator/tsconfig.json create mode 100644 packages/frames-validator/vitest.config.ts create mode 100644 packages/frames-validator/vitest.setup.ts diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json new file mode 100644 index 000000000..e944ad04b --- /dev/null +++ b/packages/frames-validator/package.json @@ -0,0 +1,39 @@ +{ + "name": "@xmtp/frames-validator", + "version": "1.0.0", + "description": "A validator for XMTP frames requests", + "main": "dist/src/index.js", + "type": "module", + "engines": { + "node": ">=18" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public", + "provenance": true + }, + "dependencies": { + "@xmtp/proto": "3.41.0-beta.2", + "@xmtp/xmtp-js": "^11.3.5" + }, + "scripts": { + "clean": "rm -rf dist", + "build": "yarn clean && tsc", + "prepublishOnly": "yarn build", + "test": "vitest run ./src" + }, + "author": "XMTP Labs ", + "license": "MIT", + "bugs": { + "url": "https://github.com/xmtp/xmtp-node-js-tools/issues" + }, + "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", + "packageManager": "yarn@4.0.0", + "devDependencies": { + "@xmtp/frames-client": "^0.1.2", + "ethers": "^6.10.0", + "vitest": "^1.0.1" + } +} diff --git a/packages/frames-validator/src/crypto.ts b/packages/frames-validator/src/crypto.ts new file mode 100644 index 000000000..c5a021aa1 --- /dev/null +++ b/packages/frames-validator/src/crypto.ts @@ -0,0 +1,6 @@ +import { webcrypto } from "crypto" + +export async function sha256(data: Uint8Array): Promise { + const hash = await webcrypto.subtle.digest("SHA-256", data) + return new Uint8Array(hash) +} diff --git a/packages/frames-validator/src/index.test.ts b/packages/frames-validator/src/index.test.ts new file mode 100644 index 000000000..f9f46556c --- /dev/null +++ b/packages/frames-validator/src/index.test.ts @@ -0,0 +1,113 @@ +import { FramesClient } from "@xmtp/frames-client" +import { fetcher, frames } from "@xmtp/proto" +import { Client, PrivateKeyBundleV2 } from "@xmtp/xmtp-js" +import { Wallet } from "ethers" +import { beforeEach, describe, expect, it } from "vitest" + +import { deserializeProtoMessage, validateFramesPost } from "." + +const { b64Decode, b64Encode } = fetcher + +function scrambleBytes(bytes: Uint8Array) { + const scrambled = new Uint8Array(bytes.length) + for (let i = 0; i < bytes.length; i++) { + scrambled[i] = bytes[bytes.length - i - 1] + } + return scrambled +} + +describe("validations", () => { + let client: Client + let framesClient: FramesClient + + const FRAME_URL = "https://frame.xyz" + const CONVERSATION_IDENTIFIER = "/xmtp/0/1234" + const MESSAGE_ID = "abcdefg" + const BUTTON_INDEX = 2 + + beforeEach(async () => { + const wallet = Wallet.createRandom() + client = await Client.create(wallet) + framesClient = new FramesClient(client) + }) + it("succeeds in the happy path", async () => { + const postData = await framesClient.signFrameAction( + FRAME_URL, + BUTTON_INDEX, + CONVERSATION_IDENTIFIER, + MESSAGE_ID, + ) + const validated = await validateFramesPost(postData) + expect(validated.verifiedWalletAddress).toEqual(client.address) + }) + + it("fails if the signature verification fails", async () => { + const postData = await framesClient.signFrameAction( + FRAME_URL, + BUTTON_INDEX, + CONVERSATION_IDENTIFIER, + MESSAGE_ID, + ) + // Monkey around with the signature + const deserialized = deserializeProtoMessage( + b64Decode(postData.trustedData.messageBytes), + ) + + if (!deserialized.signature?.ecdsaCompact?.bytes) { + throw new Error("Signature bytes are empty") + } + + deserialized.signature.ecdsaCompact.bytes = scrambleBytes( + deserialized.signature.ecdsaCompact?.bytes, + ) + const reserialized = frames.FrameAction.encode({ + signature: deserialized.signature, + actionBody: deserialized.actionBodyBytes, + signedPublicKeyBundle: deserialized.signedPublicKeyBundle, + }).finish() + + postData.trustedData.messageBytes = b64Encode( + reserialized, + 0, + reserialized.length, + ) + + expect(validateFramesPost(postData)).rejects.toThrow() + }) + + it("fails if the wallet address doesn't match", async () => { + const postData = await framesClient.signFrameAction( + FRAME_URL, + BUTTON_INDEX, + CONVERSATION_IDENTIFIER, + MESSAGE_ID, + ) + // Monkey around with the signature + const deserialized = deserializeProtoMessage( + b64Decode(postData.trustedData.messageBytes), + ) + + if (!deserialized.signedPublicKeyBundle) { + throw new Error("Public key bunlde is empty") + } + + const throwAwayWallet = Wallet.createRandom() + const wrongPublicKeyBundle = ( + await PrivateKeyBundleV2.generate(throwAwayWallet) + ).getPublicKeyBundle() + + const reserialized = frames.FrameAction.encode({ + signature: deserialized.signature, + actionBody: deserialized.actionBodyBytes, + signedPublicKeyBundle: wrongPublicKeyBundle, + }).finish() + + postData.trustedData.messageBytes = b64Encode( + reserialized, + 0, + reserialized.length, + ) + + expect(validateFramesPost(postData)).rejects.toThrow() + }) +}) diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts new file mode 100644 index 000000000..4e602b488 --- /dev/null +++ b/packages/frames-validator/src/index.ts @@ -0,0 +1,97 @@ +import { fetcher, frames } from "@xmtp/proto" +import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" + +import { sha256 } from "./crypto" +import { FramePostPayload, FramePostUntrustedData } from "./types" + +const { b64Decode } = fetcher + +export async function validateFramesPost(data: FramePostPayload) { + const { untrustedData, trustedData } = data + const { walletAddress } = untrustedData + const { messageBytes: messageBytesString } = trustedData + + const messageBytes = b64Decode(messageBytesString) + + const { actionBody, actionBodyBytes, signature, signedPublicKeyBundle } = + deserializeProtoMessage(messageBytes) + + const verifiedWalletAddress = await getVerifiedWalletAddress( + actionBodyBytes, + signature, + signedPublicKeyBundle, + ) + + if (verifiedWalletAddress !== walletAddress) { + throw new Error("Invalid wallet address") + } + + await checkUntrustedData(untrustedData, actionBody) + + return { + untrustedData, + trustedData, + verifiedWalletAddress, + } +} + +export function deserializeProtoMessage(messageBytes: Uint8Array) { + const frameAction = frames.FrameAction.decode(messageBytes) + if (!frameAction.signature || !frameAction.signedPublicKeyBundle) { + throw new Error( + "Invalid frame action: missing signature or signed public key bundle", + ) + } + const actionBody = frames.FrameActionBody.decode(frameAction.actionBody) + + return { + actionBody, + actionBodyBytes: frameAction.actionBody, + signature: new Signature(frameAction.signature), + signedPublicKeyBundle: new SignedPublicKeyBundle( + frameAction.signedPublicKeyBundle, + ), + } +} + +async function getVerifiedWalletAddress( + actionBodyBytes: Uint8Array, + signature: Signature, + signedPublicKeyBundle: SignedPublicKeyBundle, +): Promise { + const isValid = signedPublicKeyBundle.identityKey.verify( + signature, + await sha256(actionBodyBytes), + ) + + if (!isValid) { + throw new Error("Invalid signature") + } + + return signedPublicKeyBundle.walletSignatureAddress() +} + +async function checkUntrustedData( + untrusted: FramePostUntrustedData, + actionBody: frames.FrameActionBody, +) { + const { url, messageId, buttonIndex, conversationIdentifier } = untrusted + + if (new TextDecoder().decode(actionBody.frameUrl) !== url) { + throw new Error("Mismatched URL") + } + + if ( + new TextDecoder().decode(actionBody.buttonIndex) !== buttonIndex.toString() + ) { + throw new Error("Mismatched button index") + } + + if (actionBody.conversationIdentifier !== conversationIdentifier) { + throw new Error("Mismatched conversation identifier") + } + + if (actionBody.messageId !== messageId) { + throw new Error("Mismatched message identifier") + } +} diff --git a/packages/frames-validator/src/types.ts b/packages/frames-validator/src/types.ts new file mode 100644 index 000000000..0c5f5e506 --- /dev/null +++ b/packages/frames-validator/src/types.ts @@ -0,0 +1,17 @@ +export type FramePostUntrustedData = { + walletAddress: string + url: string + messageId: string + timestamp: number + buttonIndex: number + conversationIdentifier: string +} + +export type FramePostTrustedData = { + messageBytes: string +} + +export type FramePostPayload = { + untrustedData: FramePostUntrustedData + trustedData: FramePostTrustedData +} diff --git a/packages/frames-validator/tsconfig.json b/packages/frames-validator/tsconfig.json new file mode 100644 index 000000000..2ddb1f195 --- /dev/null +++ b/packages/frames-validator/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist" + }, + "includes": ["src"] +} diff --git a/packages/frames-validator/vitest.config.ts b/packages/frames-validator/vitest.config.ts new file mode 100644 index 000000000..a1ab5a8b2 --- /dev/null +++ b/packages/frames-validator/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig, mergeConfig } from "vite" +import { defineConfig as defineVitestConfig } from "vitest/config" + +// https://vitejs.dev/config/ +const viteConfig = defineConfig({ + plugins: [], +}) + +const vitestConfig = defineVitestConfig({ + test: { + globals: true, + environment: "node", + setupFiles: "./vitest.setup.ts", + }, +}) + +export default mergeConfig(viteConfig, vitestConfig) diff --git a/packages/frames-validator/vitest.setup.ts b/packages/frames-validator/vitest.setup.ts new file mode 100644 index 000000000..7c6dd095f --- /dev/null +++ b/packages/frames-validator/vitest.setup.ts @@ -0,0 +1,5 @@ +import { webcrypto } from "crypto" + +// eslint-disable-next-line +// @ts-ignore +global.crypto = webcrypto From f96f95b7652fa3cbb6dd0200a3b7aabf0c9bc9ac Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:22:54 -0800 Subject: [PATCH 02/51] Change version --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index e944ad04b..4d51956a5 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "1.0.0", + "version": "0.0.1", "description": "A validator for XMTP frames requests", "main": "dist/src/index.js", "type": "module", From 5db7c90d0fcda3954f77fe1e989f3c0e0ba10520 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Jan 2024 05:55:07 +0000 Subject: [PATCH 03/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.1.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 7 +++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 packages/frames-validator/CHANGELOG.md diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md new file mode 100644 index 000000000..c6b3bb963 --- /dev/null +++ b/packages/frames-validator/CHANGELOG.md @@ -0,0 +1,7 @@ +# @xmtp/frames-validator + +## 0.1.0 + +### Minor Changes + +- [#131](https://github.com/xmtp/xmtp-node-js-tools/pull/131) [`03a6083`](https://github.com/xmtp/xmtp-node-js-tools/commit/03a608352ec9814edda449ad75610a78ad6c4110) Thanks [@neekolas](https://github.com/neekolas)! - Initialize frames-validator package diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 4d51956a5..12b81781a 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.0.1", + "version": "0.1.0", "description": "A validator for XMTP frames requests", "main": "dist/src/index.js", "type": "module", From 70591f9f911b7cb9dbfae5187f3cc182677ee587 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 30 Jan 2024 10:58:20 -0800 Subject: [PATCH 04/51] Fix import bug --- packages/frames-validator/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts index 4e602b488..202befd07 100644 --- a/packages/frames-validator/src/index.ts +++ b/packages/frames-validator/src/index.ts @@ -1,8 +1,8 @@ import { fetcher, frames } from "@xmtp/proto" import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" -import { sha256 } from "./crypto" -import { FramePostPayload, FramePostUntrustedData } from "./types" +import { sha256 } from "./crypto.js" +import { FramePostPayload, FramePostUntrustedData } from "./types.js" const { b64Decode } = fetcher From 84f4a04b81dca16b0b7cfff1ed29bd08aca7cc99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Jan 2024 19:02:37 +0000 Subject: [PATCH 05/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.1.1 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index c6b3bb963..ae711a0df 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.1.1 + +### Patch Changes + +- [#133](https://github.com/xmtp/xmtp-node-js-tools/pull/133) [`ee73b40`](https://github.com/xmtp/xmtp-node-js-tools/commit/ee73b40f72f22d62bd3d341ce691cc30e18c3ec3) Thanks [@neekolas](https://github.com/neekolas)! - Fix import error + ## 0.1.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 12b81781a..55847559b 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.1.0", + "version": "0.1.1", "description": "A validator for XMTP frames requests", "main": "dist/src/index.js", "type": "module", From aae4289e5ac03cf8fec973486bffcf836ddce3df Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:30:07 -0600 Subject: [PATCH 06/51] Use latest version --- packages/frames-validator/package.json | 4 +-- packages/frames-validator/src/index.test.ts | 40 ++++++++++----------- packages/frames-validator/src/index.ts | 26 +++++++------- packages/frames-validator/src/types.ts | 11 +++--- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 55847559b..ad15ec06a 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -15,7 +15,7 @@ "provenance": true }, "dependencies": { - "@xmtp/proto": "3.41.0-beta.2", + "@xmtp/proto": "3.41.0-beta.5", "@xmtp/xmtp-js": "^11.3.5" }, "scripts": { @@ -32,7 +32,7 @@ "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", "packageManager": "yarn@4.0.0", "devDependencies": { - "@xmtp/frames-client": "^0.1.2", + "@xmtp/frames-client": "^0.2.0", "ethers": "^6.10.0", "vitest": "^1.0.1" } diff --git a/packages/frames-validator/src/index.test.ts b/packages/frames-validator/src/index.test.ts index f9f46556c..f8fd3f8e3 100644 --- a/packages/frames-validator/src/index.test.ts +++ b/packages/frames-validator/src/index.test.ts @@ -21,8 +21,8 @@ describe("validations", () => { let framesClient: FramesClient const FRAME_URL = "https://frame.xyz" - const CONVERSATION_IDENTIFIER = "/xmtp/0/1234" - const MESSAGE_ID = "abcdefg" + const CONVERSATION_TOPIC = "/xmtp/0/1234" + const PARTICIPANT_ACCOUNT_ADDRESSES = ["0x1234", "0x5678"] const BUTTON_INDEX = 2 beforeEach(async () => { @@ -31,23 +31,23 @@ describe("validations", () => { framesClient = new FramesClient(client) }) it("succeeds in the happy path", async () => { - const postData = await framesClient.signFrameAction( - FRAME_URL, - BUTTON_INDEX, - CONVERSATION_IDENTIFIER, - MESSAGE_ID, - ) + const postData = await framesClient.signFrameAction({ + buttonIndex: BUTTON_INDEX, + frameUrl: FRAME_URL, + conversationTopic: CONVERSATION_TOPIC, + participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, + }) const validated = await validateFramesPost(postData) expect(validated.verifiedWalletAddress).toEqual(client.address) }) it("fails if the signature verification fails", async () => { - const postData = await framesClient.signFrameAction( - FRAME_URL, - BUTTON_INDEX, - CONVERSATION_IDENTIFIER, - MESSAGE_ID, - ) + const postData = await framesClient.signFrameAction({ + buttonIndex: BUTTON_INDEX, + frameUrl: FRAME_URL, + conversationTopic: CONVERSATION_TOPIC, + participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, + }) // Monkey around with the signature const deserialized = deserializeProtoMessage( b64Decode(postData.trustedData.messageBytes), @@ -76,12 +76,12 @@ describe("validations", () => { }) it("fails if the wallet address doesn't match", async () => { - const postData = await framesClient.signFrameAction( - FRAME_URL, - BUTTON_INDEX, - CONVERSATION_IDENTIFIER, - MESSAGE_ID, - ) + const postData = await framesClient.signFrameAction({ + buttonIndex: BUTTON_INDEX, + frameUrl: FRAME_URL, + conversationTopic: CONVERSATION_TOPIC, + participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, + }) // Monkey around with the signature const deserialized = deserializeProtoMessage( b64Decode(postData.trustedData.messageBytes), diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts index 202befd07..e3fa62c54 100644 --- a/packages/frames-validator/src/index.ts +++ b/packages/frames-validator/src/index.ts @@ -29,8 +29,7 @@ export async function validateFramesPost(data: FramePostPayload) { await checkUntrustedData(untrustedData, actionBody) return { - untrustedData, - trustedData, + actionBody, verifiedWalletAddress, } } @@ -72,26 +71,29 @@ async function getVerifiedWalletAddress( } async function checkUntrustedData( - untrusted: FramePostUntrustedData, + { + url, + buttonIndex, + opaqueConversationIdentifier, + timestamp, + }: FramePostUntrustedData, actionBody: frames.FrameActionBody, ) { - const { url, messageId, buttonIndex, conversationIdentifier } = untrusted - - if (new TextDecoder().decode(actionBody.frameUrl) !== url) { + if (actionBody.frameUrl !== url) { throw new Error("Mismatched URL") } - if ( - new TextDecoder().decode(actionBody.buttonIndex) !== buttonIndex.toString() - ) { + if (actionBody.buttonIndex !== buttonIndex) { throw new Error("Mismatched button index") } - if (actionBody.conversationIdentifier !== conversationIdentifier) { + if ( + actionBody.opaqueConversationIdentifier !== opaqueConversationIdentifier + ) { throw new Error("Mismatched conversation identifier") } - if (actionBody.messageId !== messageId) { - throw new Error("Mismatched message identifier") + if (actionBody.timestamp.toNumber() !== timestamp) { + throw new Error("Mismatched timestamp") } } diff --git a/packages/frames-validator/src/types.ts b/packages/frames-validator/src/types.ts index 0c5f5e506..b7ed48b2d 100644 --- a/packages/frames-validator/src/types.ts +++ b/packages/frames-validator/src/types.ts @@ -1,10 +1,9 @@ export type FramePostUntrustedData = { - walletAddress: string - url: string - messageId: string - timestamp: number - buttonIndex: number - conversationIdentifier: string + walletAddress: string // Untrusted version of the wallet address + url: string // Frame URL. May be different from the `post_url` this is being sent to + timestamp: number // Timestamp in milliseconds + buttonIndex: number // 1-indexed button that was clicked + opaqueConversationIdentifier: string // A hash of the conversation topic and the participants } export type FramePostTrustedData = { From 41c107ec89c6790e38f14c7ece1a94fbc92f4e86 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:48:15 -0600 Subject: [PATCH 07/51] Upgrade typescript --- packages/frames-validator/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index ad15ec06a..f23c6078b 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -34,6 +34,7 @@ "devDependencies": { "@xmtp/frames-client": "^0.2.0", "ethers": "^6.10.0", + "typescript": "^5.3.3", "vitest": "^1.0.1" } } From e913cc1fbb325a55ab8dd5e7914d5e15b202c8e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 Feb 2024 19:02:21 +0000 Subject: [PATCH 08/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.2.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index ae711a0df..9b4bec3f2 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.2.0 + +### Minor Changes + +- [#140](https://github.com/xmtp/xmtp-node-js-tools/pull/140) [`4010423`](https://github.com/xmtp/xmtp-node-js-tools/commit/40104235bb8f5ab62cd98e35214d62e268816c93) Thanks [@neekolas](https://github.com/neekolas)! - Update to latest version of our protos + ## 0.1.1 ### Patch Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index f23c6078b..0310bf458 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.1.1", + "version": "0.2.0", "description": "A validator for XMTP frames requests", "main": "dist/src/index.js", "type": "module", From 6f5119938d73c69db4e2af2bdf57903925cb6d89 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 6 Feb 2024 23:27:10 -0600 Subject: [PATCH 09/51] Configure to support .cjs and esm exports --- packages/frames-validator/package.json | 17 ++++++-- packages/frames-validator/rollup.config.mjs | 43 +++++++++++++++++++++ packages/frames-validator/src/crypto.ts | 2 +- packages/frames-validator/tsconfig.json | 5 ++- 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 packages/frames-validator/rollup.config.mjs diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 0310bf458..5dade66e0 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -2,8 +2,16 @@ "name": "@xmtp/frames-validator", "version": "0.2.0", "description": "A validator for XMTP frames requests", - "main": "dist/src/index.js", - "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, "engines": { "node": ">=18" }, @@ -20,7 +28,7 @@ }, "scripts": { "clean": "rm -rf dist", - "build": "yarn clean && tsc", + "build": "yarn clean && rollup -c", "prepublishOnly": "yarn build", "test": "vitest run ./src" }, @@ -32,8 +40,11 @@ "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", "packageManager": "yarn@4.0.0", "devDependencies": { + "@rollup/plugin-typescript": "^11.1.6", "@xmtp/frames-client": "^0.2.0", "ethers": "^6.10.0", + "rollup": "^4.9.6", + "rollup-plugin-dts": "^6.1.0", "typescript": "^5.3.3", "vitest": "^1.0.1" } diff --git a/packages/frames-validator/rollup.config.mjs b/packages/frames-validator/rollup.config.mjs new file mode 100644 index 000000000..6fbea6ab4 --- /dev/null +++ b/packages/frames-validator/rollup.config.mjs @@ -0,0 +1,43 @@ +import typescript from "@rollup/plugin-typescript" +import { defineConfig } from "rollup" +import { dts } from "rollup-plugin-dts" + +const external = ["@xmtp/proto", "node:crypto", "@xmtp/xmtp-js", "long"] + +const plugins = [ + typescript({ + declaration: false, + declarationMap: false, + }), +] + +export default defineConfig([ + { + input: "src/index.ts", + output: { + file: "dist/index.js", + format: "es", + sourcemap: true, + }, + plugins, + external, + }, + { + input: "src/index.ts", + output: { + file: "dist/index.cjs", + format: "cjs", + sourcemap: true, + }, + plugins, + external, + }, + { + input: "src/index.ts", + output: { + file: "dist/index.d.ts", + format: "es", + }, + plugins: [dts()], + }, +]) diff --git a/packages/frames-validator/src/crypto.ts b/packages/frames-validator/src/crypto.ts index c5a021aa1..680a33fd0 100644 --- a/packages/frames-validator/src/crypto.ts +++ b/packages/frames-validator/src/crypto.ts @@ -1,4 +1,4 @@ -import { webcrypto } from "crypto" +import { webcrypto } from "node:crypto" export async function sha256(data: Uint8Array): Promise { const hash = await webcrypto.subtle.digest("SHA-256", data) diff --git a/packages/frames-validator/tsconfig.json b/packages/frames-validator/tsconfig.json index 2ddb1f195..f4bb6b84f 100644 --- a/packages/frames-validator/tsconfig.json +++ b/packages/frames-validator/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist" + "outDir": "dist/esm", + "composite": false }, - "includes": ["src"] + "includes": ["src/**/*"] } From 645b75d24068b65e886eec9c50a9011c6bbe1471 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 6 Feb 2024 23:40:43 -0600 Subject: [PATCH 10/51] Add README --- packages/frames-validator/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 packages/frames-validator/README.md diff --git a/packages/frames-validator/README.md b/packages/frames-validator/README.md new file mode 100644 index 000000000..3e6713716 --- /dev/null +++ b/packages/frames-validator/README.md @@ -0,0 +1,20 @@ +# Frames Validator + +A set of tools for validating POST payloads from XMTP Frames + +## Usage + +```ts +import { validateFramesPost } from "@xmtp/frames-validator" + +export function handler(requestBody: any) { + // This is an XMTP payload + if (requestBody.untrustedData?.clientType === "xmtp") { + const { verifiedWalletAddress } = await validateFramesPost(requestBody) + return doSomethingWithWalletAddress(verifiedWalletAddress) + } else { + // This is a Farcaster POST payload + return doSomethingWithFarcasterPayload(requestBody) + } +} +``` From 44ba64d971a290df07b6da30aea607233303c4c7 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Wed, 7 Feb 2024 09:45:56 -0600 Subject: [PATCH 11/51] Keep as module --- packages/frames-validator/package.json | 1 + .../frames-validator/{rollup.config.mjs => rollup.config.js} | 0 2 files changed, 1 insertion(+) rename packages/frames-validator/{rollup.config.mjs => rollup.config.js} (100%) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 5dade66e0..ca0ba7e6d 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -2,6 +2,7 @@ "name": "@xmtp/frames-validator", "version": "0.2.0", "description": "A validator for XMTP frames requests", + "type": "module", "main": "dist/index.cjs", "module": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/frames-validator/rollup.config.mjs b/packages/frames-validator/rollup.config.js similarity index 100% rename from packages/frames-validator/rollup.config.mjs rename to packages/frames-validator/rollup.config.js From ec1a4a87adb382dd11a9833e16013d3757b987fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 Feb 2024 15:54:02 +0000 Subject: [PATCH 12/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.3.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 9b4bec3f2..b63ced4db 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.3.0 + +### Minor Changes + +- [#143](https://github.com/xmtp/xmtp-node-js-tools/pull/143) [`050c529`](https://github.com/xmtp/xmtp-node-js-tools/commit/050c52986414773dba01796ed86d1ea5ec365be8) Thanks [@neekolas](https://github.com/neekolas)! - Configure to export for both Node.js and ESM + ## 0.2.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index ca0ba7e6d..199cd4522 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.2.0", + "version": "0.3.0", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 3029db97774c955dc5a984ebd3fd1b0179debaca Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 7 Feb 2024 10:07:00 -0600 Subject: [PATCH 13/51] Export all the types --- packages/frames-validator/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts index e3fa62c54..acf330fd1 100644 --- a/packages/frames-validator/src/index.ts +++ b/packages/frames-validator/src/index.ts @@ -3,6 +3,7 @@ import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" import { sha256 } from "./crypto.js" import { FramePostPayload, FramePostUntrustedData } from "./types.js" +export * from "./types.js" const { b64Decode } = fetcher From 05abd59c8cb43abb24bd9748feeb72ce4740fefb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 Feb 2024 16:12:05 +0000 Subject: [PATCH 14/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.3.1 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index b63ced4db..1bf88f719 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.3.1 + +### Patch Changes + +- [#145](https://github.com/xmtp/xmtp-node-js-tools/pull/145) [`5fb6232`](https://github.com/xmtp/xmtp-node-js-tools/commit/5fb623267505a3e964281e3527c76c6a1c752c14) Thanks [@neekolas](https://github.com/neekolas)! - Export all the types + ## 0.3.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 199cd4522..70077791d 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.3.0", + "version": "0.3.1", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 3bb8bf941978132eb066d7cf954e6b787a1abee4 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Sat, 10 Feb 2024 09:54:05 -0800 Subject: [PATCH 15/51] Add OpenFrames support --- packages/frames-validator/package.json | 5 +- packages/frames-validator/src/index.ts | 102 +------------------ packages/frames-validator/src/openFrames.ts | 104 ++++++++++++++++++++ packages/frames-validator/src/types.ts | 23 +++-- packages/frames-validator/src/validation.ts | 101 +++++++++++++++++++ 5 files changed, 224 insertions(+), 111 deletions(-) create mode 100644 packages/frames-validator/src/openFrames.ts create mode 100644 packages/frames-validator/src/validation.ts diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 70077791d..1358ca66d 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -24,7 +24,7 @@ "provenance": true }, "dependencies": { - "@xmtp/proto": "3.41.0-beta.5", + "@xmtp/proto": "3.41.0-beta.6", "@xmtp/xmtp-js": "^11.3.5" }, "scripts": { @@ -41,8 +41,9 @@ "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", "packageManager": "yarn@4.0.0", "devDependencies": { + "@open-frames/types": "^0.0.5", "@rollup/plugin-typescript": "^11.1.6", - "@xmtp/frames-client": "^0.2.0", + "@xmtp/frames-client": "^0.2.2", "ethers": "^6.10.0", "rollup": "^4.9.6", "rollup-plugin-dts": "^6.1.0", diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts index acf330fd1..a43697110 100644 --- a/packages/frames-validator/src/index.ts +++ b/packages/frames-validator/src/index.ts @@ -1,100 +1,2 @@ -import { fetcher, frames } from "@xmtp/proto" -import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" - -import { sha256 } from "./crypto.js" -import { FramePostPayload, FramePostUntrustedData } from "./types.js" -export * from "./types.js" - -const { b64Decode } = fetcher - -export async function validateFramesPost(data: FramePostPayload) { - const { untrustedData, trustedData } = data - const { walletAddress } = untrustedData - const { messageBytes: messageBytesString } = trustedData - - const messageBytes = b64Decode(messageBytesString) - - const { actionBody, actionBodyBytes, signature, signedPublicKeyBundle } = - deserializeProtoMessage(messageBytes) - - const verifiedWalletAddress = await getVerifiedWalletAddress( - actionBodyBytes, - signature, - signedPublicKeyBundle, - ) - - if (verifiedWalletAddress !== walletAddress) { - throw new Error("Invalid wallet address") - } - - await checkUntrustedData(untrustedData, actionBody) - - return { - actionBody, - verifiedWalletAddress, - } -} - -export function deserializeProtoMessage(messageBytes: Uint8Array) { - const frameAction = frames.FrameAction.decode(messageBytes) - if (!frameAction.signature || !frameAction.signedPublicKeyBundle) { - throw new Error( - "Invalid frame action: missing signature or signed public key bundle", - ) - } - const actionBody = frames.FrameActionBody.decode(frameAction.actionBody) - - return { - actionBody, - actionBodyBytes: frameAction.actionBody, - signature: new Signature(frameAction.signature), - signedPublicKeyBundle: new SignedPublicKeyBundle( - frameAction.signedPublicKeyBundle, - ), - } -} - -async function getVerifiedWalletAddress( - actionBodyBytes: Uint8Array, - signature: Signature, - signedPublicKeyBundle: SignedPublicKeyBundle, -): Promise { - const isValid = signedPublicKeyBundle.identityKey.verify( - signature, - await sha256(actionBodyBytes), - ) - - if (!isValid) { - throw new Error("Invalid signature") - } - - return signedPublicKeyBundle.walletSignatureAddress() -} - -async function checkUntrustedData( - { - url, - buttonIndex, - opaqueConversationIdentifier, - timestamp, - }: FramePostUntrustedData, - actionBody: frames.FrameActionBody, -) { - if (actionBody.frameUrl !== url) { - throw new Error("Mismatched URL") - } - - if (actionBody.buttonIndex !== buttonIndex) { - throw new Error("Mismatched button index") - } - - if ( - actionBody.opaqueConversationIdentifier !== opaqueConversationIdentifier - ) { - throw new Error("Mismatched conversation identifier") - } - - if (actionBody.timestamp.toNumber() !== timestamp) { - throw new Error("Mismatched timestamp") - } -} +export * from "./openFrames.js" +export * from "./validation.js" diff --git a/packages/frames-validator/src/openFrames.ts b/packages/frames-validator/src/openFrames.ts new file mode 100644 index 000000000..02ac142f1 --- /dev/null +++ b/packages/frames-validator/src/openFrames.ts @@ -0,0 +1,104 @@ +import { + OpenFramesRequest, + RequestValidator, + ValidationResponse, +} from "@open-frames/types" + +import { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types" +import { validateFramesPost } from "./validation" + +const Validator: RequestValidator< + XmtpOpenFramesRequest, + XmtpValidationResponse, + "xmtp" +> = { + protocolIdentifier: "xmtp", + minProtocolVersionDate: "2024-02-09", + minProtocolVersion(): string { + return "xxxxxxx.xxxxxxxxxxxxxxxxxxx@xxxxxx.xxxxxxxxxxxxxxxxxxxxxxxx" + }, + isSupported(payload: OpenFramesRequest): payload is XmtpOpenFramesRequest { + if (!payload.clientProtocol) { + return false + } + + const [protocol, version] = payload.clientProtocol.split("@") + if (!protocol || !version) { + return false + } + + const isCorrectClientProtocol = protocol === "xmtp" + const isCorrectVersion = version >= this.minProtocolVersionDate + const isTrustedDataValid = + typeof payload.trustedData?.messageBytes === "string" + + return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid + }, + async validate( + payload: XmtpOpenFramesRequest, + ): Promise< + ValidationResponse + > { + try { + const validationResponse = await validateFramesPost(payload) + return { + isValid: true, + clientProtocol: payload.clientProtocol, + message: validationResponse, + } + } catch (error) { + return { + isValid: false, + } + } + }, +} + +export class XmtpValidator + implements + RequestValidator +{ + readonly protocolIdentifier = "xmtp" + readonly minProtocolVersionDate = "2024-02-09" + + minProtocolVersion(): string { + return `${this.protocolIdentifier}@${this.minProtocolVersionDate}` + } + + isSupported(payload: OpenFramesRequest): payload is XmtpOpenFramesRequest { + if (!payload.clientProtocol) { + return false + } + + const [protocol, version] = payload.clientProtocol.split("@") + if (!protocol || !version) { + return false + } + + const isCorrectClientProtocol = protocol === "xmtp" + const isCorrectVersion = version >= this.minProtocolVersionDate + const isTrustedDataValid = + typeof payload.trustedData?.messageBytes === "string" + + return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid + } + + async validate( + payload: XmtpOpenFramesRequest, + ): Promise< + ValidationResponse + > { + try { + const validationResponse = await validateFramesPost(payload) + return { + isValid: true, + clientProtocol: payload.clientProtocol, + message: validationResponse, + } + } catch (error) { + return { + isValid: false, + } + } + } +} diff --git a/packages/frames-validator/src/types.ts b/packages/frames-validator/src/types.ts index b7ed48b2d..e4ac1a56b 100644 --- a/packages/frames-validator/src/types.ts +++ b/packages/frames-validator/src/types.ts @@ -1,16 +1,21 @@ -export type FramePostUntrustedData = { +import type { + OpenFramesTrustedData, + OpenFramesUntrustedData, +} from "@open-frames/types" +import { frames } from "@xmtp/proto" + +export type UntrustedData = OpenFramesUntrustedData & { walletAddress: string // Untrusted version of the wallet address - url: string // Frame URL. May be different from the `post_url` this is being sent to - timestamp: number // Timestamp in milliseconds - buttonIndex: number // 1-indexed button that was clicked opaqueConversationIdentifier: string // A hash of the conversation topic and the participants } -export type FramePostTrustedData = { - messageBytes: string +export type XmtpOpenFramesRequest = { + clientProtocol: `xmtp@${string}` + untrustedData: UntrustedData + trustedData: OpenFramesTrustedData } -export type FramePostPayload = { - untrustedData: FramePostUntrustedData - trustedData: FramePostTrustedData +export type XmtpValidationResponse = { + actionBody: frames.FrameActionBody + verifiedWalletAddress: string } diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts new file mode 100644 index 000000000..ef27df95b --- /dev/null +++ b/packages/frames-validator/src/validation.ts @@ -0,0 +1,101 @@ +import { fetcher, frames } from "@xmtp/proto" +import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" + +import { sha256 } from "./crypto.js" +import { + UntrustedData, + XmtpOpenFramesRequest, + XmtpValidationResponse, +} from "./types.js" +export * from "./types.js" + +const { b64Decode } = fetcher + +export async function validateFramesPost( + data: XmtpOpenFramesRequest, +): Promise { + const { untrustedData, trustedData } = data + const { walletAddress } = untrustedData + const { messageBytes: messageBytesString } = trustedData + + const messageBytes = b64Decode(messageBytesString) + + const { actionBody, actionBodyBytes, signature, signedPublicKeyBundle } = + deserializeProtoMessage(messageBytes) + + const verifiedWalletAddress = await getVerifiedWalletAddress( + actionBodyBytes, + signature, + signedPublicKeyBundle, + ) + + if (verifiedWalletAddress !== walletAddress) { + throw new Error("Invalid wallet address") + } + + await checkUntrustedData(untrustedData, actionBody) + + return { + actionBody, + verifiedWalletAddress, + } +} + +export function deserializeProtoMessage(messageBytes: Uint8Array) { + const frameAction = frames.FrameAction.decode(messageBytes) + if (!frameAction.signature || !frameAction.signedPublicKeyBundle) { + throw new Error( + "Invalid frame action: missing signature or signed public key bundle", + ) + } + const actionBody = frames.FrameActionBody.decode(frameAction.actionBody) + + return { + actionBody, + actionBodyBytes: frameAction.actionBody, + signature: new Signature(frameAction.signature), + signedPublicKeyBundle: new SignedPublicKeyBundle( + frameAction.signedPublicKeyBundle, + ), + } +} + +async function getVerifiedWalletAddress( + actionBodyBytes: Uint8Array, + signature: Signature, + signedPublicKeyBundle: SignedPublicKeyBundle, +): Promise { + const isValid = signedPublicKeyBundle.identityKey.verify( + signature, + await sha256(actionBodyBytes), + ) + + if (!isValid) { + throw new Error("Invalid signature") + } + + return signedPublicKeyBundle.walletSignatureAddress() +} + +async function checkUntrustedData( + { url, buttonIndex, opaqueConversationIdentifier, timestamp }: UntrustedData, + actionBody: frames.FrameActionBody, +) { + if (actionBody.frameUrl !== url) { + throw new Error("Mismatched URL") + } + + if (actionBody.buttonIndex !== buttonIndex) { + throw new Error("Mismatched button index") + } + + if ( + actionBody.opaqueConversationIdentifier !== opaqueConversationIdentifier + ) { + throw new Error("Mismatched conversation identifier") + } + + if (actionBody.timestamp.toNumber() !== timestamp) { + throw new Error("Mismatched timestamp") + } +} From 4d4d6de29cfc7a64822acd21bd36b4aa4aa1fa3f Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Sat, 10 Feb 2024 09:58:14 -0800 Subject: [PATCH 16/51] Update types version --- packages/frames-validator/package.json | 2 +- packages/frames-validator/src/openFrames.ts | 47 --------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 1358ca66d..3dc312992 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -41,7 +41,7 @@ "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", "packageManager": "yarn@4.0.0", "devDependencies": { - "@open-frames/types": "^0.0.5", + "@open-frames/types": "^0.0.6", "@rollup/plugin-typescript": "^11.1.6", "@xmtp/frames-client": "^0.2.2", "ethers": "^6.10.0", diff --git a/packages/frames-validator/src/openFrames.ts b/packages/frames-validator/src/openFrames.ts index 02ac142f1..acdda04c3 100644 --- a/packages/frames-validator/src/openFrames.ts +++ b/packages/frames-validator/src/openFrames.ts @@ -7,53 +7,6 @@ import { import { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types" import { validateFramesPost } from "./validation" -const Validator: RequestValidator< - XmtpOpenFramesRequest, - XmtpValidationResponse, - "xmtp" -> = { - protocolIdentifier: "xmtp", - minProtocolVersionDate: "2024-02-09", - minProtocolVersion(): string { - return "xxxxxxx.xxxxxxxxxxxxxxxxxxx@xxxxxx.xxxxxxxxxxxxxxxxxxxxxxxx" - }, - isSupported(payload: OpenFramesRequest): payload is XmtpOpenFramesRequest { - if (!payload.clientProtocol) { - return false - } - - const [protocol, version] = payload.clientProtocol.split("@") - if (!protocol || !version) { - return false - } - - const isCorrectClientProtocol = protocol === "xmtp" - const isCorrectVersion = version >= this.minProtocolVersionDate - const isTrustedDataValid = - typeof payload.trustedData?.messageBytes === "string" - - return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid - }, - async validate( - payload: XmtpOpenFramesRequest, - ): Promise< - ValidationResponse - > { - try { - const validationResponse = await validateFramesPost(payload) - return { - isValid: true, - clientProtocol: payload.clientProtocol, - message: validationResponse, - } - } catch (error) { - return { - isValid: false, - } - } - }, -} - export class XmtpValidator implements RequestValidator From 6270172d3e8e0211ef01b9dfffe254eca2ba7aae Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:15:24 -0800 Subject: [PATCH 17/51] Remove xmtp-js dep --- packages/frames-validator/package.json | 5 +- packages/frames-validator/src/crypto.ts | 6 - packages/frames-validator/src/utils.ts | 140 ++++++++++++++++++++ packages/frames-validator/src/validation.ts | 28 ++-- 4 files changed, 154 insertions(+), 25 deletions(-) delete mode 100644 packages/frames-validator/src/crypto.ts create mode 100644 packages/frames-validator/src/utils.ts diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 3dc312992..9bd7ca65f 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -24,8 +24,10 @@ "provenance": true }, "dependencies": { + "@noble/hashes": "^1.3.3", + "@noble/secp256k1": "^2.0.0", "@xmtp/proto": "3.41.0-beta.6", - "@xmtp/xmtp-js": "^11.3.5" + "viem": "^2.7.8" }, "scripts": { "clean": "rm -rf dist", @@ -44,6 +46,7 @@ "@open-frames/types": "^0.0.6", "@rollup/plugin-typescript": "^11.1.6", "@xmtp/frames-client": "^0.2.2", + "@xmtp/xmtp-js": "^11.3.5", "ethers": "^6.10.0", "rollup": "^4.9.6", "rollup-plugin-dts": "^6.1.0", diff --git a/packages/frames-validator/src/crypto.ts b/packages/frames-validator/src/crypto.ts deleted file mode 100644 index 680a33fd0..000000000 --- a/packages/frames-validator/src/crypto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { webcrypto } from "node:crypto" - -export async function sha256(data: Uint8Array): Promise { - const hash = await webcrypto.subtle.digest("SHA-256", data) - return new Uint8Array(hash) -} diff --git a/packages/frames-validator/src/utils.ts b/packages/frames-validator/src/utils.ts new file mode 100644 index 000000000..9d14a7850 --- /dev/null +++ b/packages/frames-validator/src/utils.ts @@ -0,0 +1,140 @@ +import { sha256 } from "@noble/hashes/sha256" +import * as secp from "@noble/secp256k1" +import { publicKey, signature } from "@xmtp/proto" +import { SignedPublicKeyBundle } from "@xmtp/xmtp-js" +import { + bytesToHex as viemBytesToHex, + getAddress, + hashMessage, + keccak256, +} from "viem/utils" +const bytesToHex = secp.etc.bytesToHex + +export type ECDSACompactWithRecovery = { + bytes: Uint8Array // compact representation [ R || S ], 64 bytes + recovery: number // recovery bit +} + +export function hexToBytes(s: string): Uint8Array { + if (s.startsWith("0x")) { + s = s.slice(2) + } + const bytes = new Uint8Array(s.length / 2) + for (let i = 0; i < bytes.length; i++) { + const j = i * 2 + bytes[i] = Number.parseInt(s.slice(j, j + 2), 16) + } + return bytes +} + +function ecdsaCheck(sig: ECDSACompactWithRecovery): void { + if (sig.bytes.length !== 64) { + throw new Error(`invalid signature length: ${sig.bytes.length}`) + } + if (sig.recovery !== 0 && sig.recovery !== 1) { + throw new Error(`invalid recovery bit: ${sig.recovery}`) + } +} + +function extractSignature( + signature: signature.Signature, +): ECDSACompactWithRecovery { + if (signature.ecdsaCompact?.bytes) { + ecdsaCheck(signature.ecdsaCompact) + return signature.ecdsaCompact + } else if (signature.walletEcdsaCompact?.bytes) { + ecdsaCheck(signature.walletEcdsaCompact) + return signature.walletEcdsaCompact + } else { + throw new Error("invalid signature") + } +} + +function walletSignatureText(keyBytes: Uint8Array): string { + return ( + "XMTP : Create Identity\n" + + `${bytesToHex(keyBytes)}\n` + + "\n" + + "For more info: https://xmtp.org/signatures/" + ) +} + +function validateSignedPublicKeyBundle( + bundle: publicKey.SignedPublicKeyBundle, +): bundle is SignedPublicKeyBundle { + if (!bundle.identityKey?.keyBytes) { + return false + } + if (!bundle.preKey?.keyBytes) { + return false + } + return true +} + +export function verifyIdentityKeySignature( + message: Uint8Array, + sig: signature.Signature, + bundle: publicKey.SignedPublicKeyBundle, +) { + if (!validateSignedPublicKeyBundle(bundle)) { + throw new Error("Invalid public key bundle") + } + if (!sig.ecdsaCompact?.bytes) { + throw new Error("Missing ECDSA compact") + } + const pubKey = publicKey.UnsignedPublicKey.decode(bundle.identityKey.keyBytes) + if (!pubKey.secp256k1Uncompressed?.bytes) { + throw new Error("Missing key bytes") + } + + const digest = sha256(message) + const isVerified = secp.verify( + sig.ecdsaCompact.bytes, + digest, + pubKey.secp256k1Uncompressed.bytes, + ) + if (!isVerified) { + throw new Error("Invalid signature") + } +} + +function computeAddress(bytes: Uint8Array) { + const publicKey = viemBytesToHex(bytes.slice(1)) as `0x${string}` + const hash = keccak256(publicKey) + const address = hash.substring(hash.length - 40) + return getAddress(`0x${address}`) +} + +function recoverWalletAddress( + messageString: string, + sig: ECDSACompactWithRecovery, +) { + const digest = hexToBytes(hashMessage(messageString)) + const pubKey = secp.Signature.fromCompact(sig.bytes) + .addRecoveryBit(sig.recovery) + .recoverPublicKey(digest) + .toRawBytes(false) + + return computeAddress(pubKey) +} + +export async function verifyWalletSignature( + publicKeyBundle: publicKey.SignedPublicKeyBundle, +) { + if (!validateSignedPublicKeyBundle(publicKeyBundle)) { + throw new Error("Invalid public key bundle") + } + const toVerify = extractSignature(publicKeyBundle.identityKey.signature) + + const signatureText = walletSignatureText( + publicKeyBundle.identityKey.keyBytes, + ) + + const walletAddress = recoverWalletAddress(signatureText, toVerify) + + if (!walletAddress) { + throw new Error("Could not recover wallet address") + } + + return walletAddress +} diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index ef27df95b..914955128 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -1,12 +1,11 @@ -import { fetcher, frames } from "@xmtp/proto" -import { Signature, SignedPublicKeyBundle } from "@xmtp/xmtp-js" +import { fetcher, frames, publicKey, signature } from "@xmtp/proto" -import { sha256 } from "./crypto.js" import { UntrustedData, XmtpOpenFramesRequest, XmtpValidationResponse, } from "./types.js" +import { verifyIdentityKeySignature, verifyWalletSignature } from "./utils.js" export * from "./types.js" const { b64Decode } = fetcher @@ -30,6 +29,7 @@ export async function validateFramesPost( ) if (verifiedWalletAddress !== walletAddress) { + console.log(`${verifiedWalletAddress} !== ${walletAddress}`) throw new Error("Invalid wallet address") } @@ -53,28 +53,20 @@ export function deserializeProtoMessage(messageBytes: Uint8Array) { return { actionBody, actionBodyBytes: frameAction.actionBody, - signature: new Signature(frameAction.signature), - signedPublicKeyBundle: new SignedPublicKeyBundle( - frameAction.signedPublicKeyBundle, - ), + signature: frameAction.signature, + signedPublicKeyBundle: frameAction.signedPublicKeyBundle, } } async function getVerifiedWalletAddress( actionBodyBytes: Uint8Array, - signature: Signature, - signedPublicKeyBundle: SignedPublicKeyBundle, + signature: signature.Signature, + signedPublicKeyBundle: publicKey.SignedPublicKeyBundle, ): Promise { - const isValid = signedPublicKeyBundle.identityKey.verify( - signature, - await sha256(actionBodyBytes), - ) - - if (!isValid) { - throw new Error("Invalid signature") - } + const walletAddress = await verifyWalletSignature(signedPublicKeyBundle) + verifyIdentityKeySignature(actionBodyBytes, signature, signedPublicKeyBundle) - return signedPublicKeyBundle.walletSignatureAddress() + return walletAddress } async function checkUntrustedData( From ec03d36f018f436b77d88ee6e8a5db4740adab42 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:03:51 -0800 Subject: [PATCH 18/51] Run yarn --- packages/frames-validator/src/utils.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/frames-validator/src/utils.ts b/packages/frames-validator/src/utils.ts index 9d14a7850..6887f5c98 100644 --- a/packages/frames-validator/src/utils.ts +++ b/packages/frames-validator/src/utils.ts @@ -15,6 +15,7 @@ export type ECDSACompactWithRecovery = { recovery: number // recovery bit } +// hexToBytes implementation that is compatible with `xmtp-js`'s implementation export function hexToBytes(s: string): Uint8Array { if (s.startsWith("0x")) { s = s.slice(2) @@ -27,6 +28,7 @@ export function hexToBytes(s: string): Uint8Array { return bytes } +// Ensure the signature is valid function ecdsaCheck(sig: ECDSACompactWithRecovery): void { if (sig.bytes.length !== 64) { throw new Error(`invalid signature length: ${sig.bytes.length}`) @@ -36,6 +38,8 @@ function ecdsaCheck(sig: ECDSACompactWithRecovery): void { } } +// Get the signature bytes from a Signature proto message, whether it is wallet signed or signed by +// an XMTP key function extractSignature( signature: signature.Signature, ): ECDSACompactWithRecovery { @@ -50,6 +54,7 @@ function extractSignature( } } +// Directly copied from `xmtp-js` function walletSignatureText(keyBytes: Uint8Array): string { return ( "XMTP : Create Identity\n" + @@ -59,6 +64,7 @@ function walletSignatureText(keyBytes: Uint8Array): string { ) } +// Ensure that the `SignedPublicKeyBundle` has the required fields function validateSignedPublicKeyBundle( bundle: publicKey.SignedPublicKeyBundle, ): bundle is SignedPublicKeyBundle { @@ -71,6 +77,12 @@ function validateSignedPublicKeyBundle( return true } +/** + * Validate that a message was signed by the identity key in a `SignedPublicKeyBundle` + * @param message Uint8array + * @param sig signature.Signature + * @param bundle publicKey.SignedPublicKeyBundle + */ export function verifyIdentityKeySignature( message: Uint8Array, sig: signature.Signature, @@ -118,6 +130,11 @@ function recoverWalletAddress( return computeAddress(pubKey) } +/** + * Retrieve the wallet address from a `SignedPublicKeyBundle` proto + * @param publicKeyBundle + * @returns string wallet address + */ export async function verifyWalletSignature( publicKeyBundle: publicKey.SignedPublicKeyBundle, ) { From a9b956e924d7f0ad5d5b38c9bb8488a285d233d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 14 Feb 2024 07:35:07 +0000 Subject: [PATCH 19/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.4.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 1bf88f719..6ba02ddef 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.4.0 + +### Minor Changes + +- [#147](https://github.com/xmtp/xmtp-node-js-tools/pull/147) [`9ad92d8`](https://github.com/xmtp/xmtp-node-js-tools/commit/9ad92d801ce58a0610078016640a4e611b73e662) Thanks [@neekolas](https://github.com/neekolas)! - Adds support for an Open Frames validator + ## 0.3.1 ### Patch Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 9bd7ca65f..766c7b710 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.3.1", + "version": "0.4.0", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From c458e36f71ea91796651489e95602649c70b34b9 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:17:21 -0800 Subject: [PATCH 20/51] Use noble curves --- packages/frames-validator/package.json | 2 +- packages/frames-validator/src/utils.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 766c7b710..418105190 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@noble/hashes": "^1.3.3", - "@noble/secp256k1": "^2.0.0", + "@noble/curves": "^1.3.0", "@xmtp/proto": "3.41.0-beta.6", "viem": "^2.7.8" }, diff --git a/packages/frames-validator/src/utils.ts b/packages/frames-validator/src/utils.ts index 6887f5c98..febcc51ce 100644 --- a/packages/frames-validator/src/utils.ts +++ b/packages/frames-validator/src/utils.ts @@ -1,5 +1,6 @@ +import { bytesToHex } from "@noble/curves/abstract/utils" +import { secp256k1 } from "@noble/curves/secp256k1" import { sha256 } from "@noble/hashes/sha256" -import * as secp from "@noble/secp256k1" import { publicKey, signature } from "@xmtp/proto" import { SignedPublicKeyBundle } from "@xmtp/xmtp-js" import { @@ -8,7 +9,6 @@ import { hashMessage, keccak256, } from "viem/utils" -const bytesToHex = secp.etc.bytesToHex export type ECDSACompactWithRecovery = { bytes: Uint8Array // compact representation [ R || S ], 64 bytes @@ -100,7 +100,7 @@ export function verifyIdentityKeySignature( } const digest = sha256(message) - const isVerified = secp.verify( + const isVerified = secp256k1.verify( sig.ecdsaCompact.bytes, digest, pubKey.secp256k1Uncompressed.bytes, @@ -122,7 +122,7 @@ function recoverWalletAddress( sig: ECDSACompactWithRecovery, ) { const digest = hexToBytes(hashMessage(messageString)) - const pubKey = secp.Signature.fromCompact(sig.bytes) + const pubKey = secp256k1.Signature.fromCompact(sig.bytes) .addRecoveryBit(sig.recovery) .recoverPublicKey(digest) .toRawBytes(false) @@ -135,7 +135,7 @@ function recoverWalletAddress( * @param publicKeyBundle * @returns string wallet address */ -export async function verifyWalletSignature( +export function verifyWalletSignature( publicKeyBundle: publicKey.SignedPublicKeyBundle, ) { if (!validateSignedPublicKeyBundle(publicKeyBundle)) { From 431efb26592aca825ac62a9e71e24b512fe359c1 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:19:49 -0800 Subject: [PATCH 21/51] Remove unused await --- packages/frames-validator/src/validation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index 914955128..43ec4c91e 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -63,7 +63,7 @@ async function getVerifiedWalletAddress( signature: signature.Signature, signedPublicKeyBundle: publicKey.SignedPublicKeyBundle, ): Promise { - const walletAddress = await verifyWalletSignature(signedPublicKeyBundle) + const walletAddress = verifyWalletSignature(signedPublicKeyBundle) verifyIdentityKeySignature(actionBodyBytes, signature, signedPublicKeyBundle) return walletAddress From 03c651e3eca8a453e791d5d9d3707ca04a23f8b1 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:22:31 -0800 Subject: [PATCH 22/51] Update lock file --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 418105190..61f3ce182 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -24,8 +24,8 @@ "provenance": true }, "dependencies": { - "@noble/hashes": "^1.3.3", "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", "@xmtp/proto": "3.41.0-beta.6", "viem": "^2.7.8" }, From 9058d902b25f70995e3ba0920af4e26f162e28b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 16 Feb 2024 19:25:16 +0000 Subject: [PATCH 23/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.5.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 6ba02ddef..2fc254913 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.5.0 + +### Minor Changes + +- [#154](https://github.com/xmtp/xmtp-node-js-tools/pull/154) [`7530777`](https://github.com/xmtp/xmtp-node-js-tools/commit/7530777be8e863a87bc5cad6136db8202eb9bea7) Thanks [@neekolas](https://github.com/neekolas)! - Switch out encryption library for better commonjs support + ## 0.4.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 61f3ce182..7d1ede53d 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.4.0", + "version": "0.5.0", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From cd727940cf235d3eb9436ba6cc9132e231ffbd5c Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Fri, 23 Feb 2024 07:56:28 -0800 Subject: [PATCH 24/51] Upgrade xmtp proto --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 7d1ede53d..dc1368957 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -26,7 +26,7 @@ "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.3.3", - "@xmtp/proto": "3.41.0-beta.6", + "@xmtp/proto": "3.44.0", "viem": "^2.7.8" }, "scripts": { From d16d7f530816ca87bc85df7c244df41da3d5caea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 23 Feb 2024 16:00:02 +0000 Subject: [PATCH 25/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.5.1 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 2fc254913..277f2ca37 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.5.1 + +### Patch Changes + +- [#161](https://github.com/xmtp/xmtp-node-js-tools/pull/161) [`0c3cbb8`](https://github.com/xmtp/xmtp-node-js-tools/commit/0c3cbb8fb3aa392ec72787e1512d177c7c49a011) Thanks [@neekolas](https://github.com/neekolas)! - Upgrade xmtp proto + ## 0.5.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index dc1368957..411dd3aad 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.5.0", + "version": "0.5.1", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 1bb9f18dc1c3e24b3661a01aa67576d3df11ac70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:02:21 +0000 Subject: [PATCH 26/51] build(deps): Bump @xmtp/xmtp-js from 11.3.5 to 11.3.12 Bumps [@xmtp/xmtp-js](https://github.com/xmtp/xmtp-js) from 11.3.5 to 11.3.12. - [Release notes](https://github.com/xmtp/xmtp-js/releases) - [Commits](https://github.com/xmtp/xmtp-js/compare/v11.3.5...v11.3.12) --- updated-dependencies: - dependency-name: "@xmtp/xmtp-js" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 411dd3aad..eaa655b57 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -46,7 +46,7 @@ "@open-frames/types": "^0.0.6", "@rollup/plugin-typescript": "^11.1.6", "@xmtp/frames-client": "^0.2.2", - "@xmtp/xmtp-js": "^11.3.5", + "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", "rollup": "^4.9.6", "rollup-plugin-dts": "^6.1.0", From 732b0a77443d31e1d8e2a28405b0e1bdfb135fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:01:44 +0000 Subject: [PATCH 27/51] build(deps-dev): Bump rollup from 4.9.6 to 4.12.0 Bumps [rollup](https://github.com/rollup/rollup) from 4.9.6 to 4.12.0. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.9.6...v4.12.0) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index eaa655b57..73be08799 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -48,7 +48,7 @@ "@xmtp/frames-client": "^0.2.2", "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", - "rollup": "^4.9.6", + "rollup": "^4.12.0", "rollup-plugin-dts": "^6.1.0", "typescript": "^5.3.3", "vitest": "^1.0.1" From 3ee0b623b1282fe29778643deb6f5429dbe9e7b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:11:12 +0000 Subject: [PATCH 28/51] build(deps): Bump viem from 2.7.8 to 2.7.14 Bumps [viem](https://github.com/wevm/viem) from 2.7.8 to 2.7.14. - [Release notes](https://github.com/wevm/viem/releases) - [Commits](https://github.com/wevm/viem/compare/viem@2.7.8...viem@2.7.14) --- updated-dependencies: - dependency-name: viem dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 73be08799..23437dd2b 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -27,7 +27,7 @@ "@noble/curves": "^1.3.0", "@noble/hashes": "^1.3.3", "@xmtp/proto": "3.44.0", - "viem": "^2.7.8" + "viem": "^2.7.14" }, "scripts": { "clean": "rm -rf dist", From 791409961299ce832e157d8bbe384c6a8a5ee377 Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 28 Feb 2024 19:35:34 -0700 Subject: [PATCH 29/51] Add State field to be in line with of spec --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 23437dd2b..fd565c2aa 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -26,7 +26,7 @@ "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.3.3", - "@xmtp/proto": "3.44.0", + "@xmtp/proto": "3.45.0", "viem": "^2.7.14" }, "scripts": { From 8243d33d52059fca9f92ad79f6e962a4e44b04d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Mar 2024 01:19:46 +0000 Subject: [PATCH 30/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.5.2 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 277f2ca37..661060213 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.5.2 + +### Patch Changes + +- [#169](https://github.com/xmtp/xmtp-node-js-tools/pull/169) [`ea52fb6`](https://github.com/xmtp/xmtp-node-js-tools/commit/ea52fb63562d611307c7005c8fba472bc286e7e7) Thanks [@neekolas](https://github.com/neekolas)! - Add state field + ## 0.5.1 ### Patch Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index fd565c2aa..98057342e 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.5.1", + "version": "0.5.2", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 68760109dcf4c3114468a5e82f4391d18551ab5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:56:16 +0000 Subject: [PATCH 31/51] build(deps): Bump @noble/hashes from 1.3.3 to 1.4.0 Bumps [@noble/hashes](https://github.com/paulmillr/noble-hashes) from 1.3.3 to 1.4.0. - [Release notes](https://github.com/paulmillr/noble-hashes/releases) - [Commits](https://github.com/paulmillr/noble-hashes/compare/1.3.3...1.4.0) --- updated-dependencies: - dependency-name: "@noble/hashes" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 98057342e..040d09ccc 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", + "@noble/hashes": "^1.4.0", "@xmtp/proto": "3.45.0", "viem": "^2.7.14" }, From 5e2f6243a0ebf97135c8ab1ab3ce435fdfb3bbb0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:45:10 +0000 Subject: [PATCH 32/51] build(deps-dev): Bump rollup from 4.12.0 to 4.13.0 Bumps [rollup](https://github.com/rollup/rollup) from 4.12.0 to 4.13.0. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.12.0...v4.13.0) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 040d09ccc..e9ade7f28 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -48,7 +48,7 @@ "@xmtp/frames-client": "^0.2.2", "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", - "rollup": "^4.12.0", + "rollup": "^4.13.0", "rollup-plugin-dts": "^6.1.0", "typescript": "^5.3.3", "vitest": "^1.0.1" From 3c12886d3466cc55b4d8ad69280cddb37709bfc5 Mon Sep 17 00:00:00 2001 From: Alex Risch Date: Wed, 27 Mar 2024 19:19:05 -0600 Subject: [PATCH 33/51] Update packages Updated packages Added state verification --- packages/frames-validator/package.json | 4 ++-- packages/frames-validator/src/validation.ts | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index e9ade7f28..2e43d44b9 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -43,9 +43,9 @@ "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", "packageManager": "yarn@4.0.0", "devDependencies": { - "@open-frames/types": "^0.0.6", + "@open-frames/types": "^0.0.7", "@rollup/plugin-typescript": "^11.1.6", - "@xmtp/frames-client": "^0.2.2", + "@xmtp/frames-client": "^0.4.3", "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", "rollup": "^4.13.0", diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index 43ec4c91e..aaa7b3519 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -70,7 +70,13 @@ async function getVerifiedWalletAddress( } async function checkUntrustedData( - { url, buttonIndex, opaqueConversationIdentifier, timestamp }: UntrustedData, + { + url, + buttonIndex, + opaqueConversationIdentifier, + timestamp, + state, + }: UntrustedData, actionBody: frames.FrameActionBody, ) { if (actionBody.frameUrl !== url) { @@ -90,4 +96,8 @@ async function checkUntrustedData( if (actionBody.timestamp.toNumber() !== timestamp) { throw new Error("Mismatched timestamp") } + + if (actionBody.state !== state) { + throw new Error("Mismatched state") + } } From d6f44d310bcea3e1c78a12f0755c64bd1c305059 Mon Sep 17 00:00:00 2001 From: Alex Risch Date: Thu, 28 Mar 2024 11:13:07 -0600 Subject: [PATCH 34/51] feat: Check untrusted data Checks for state and inputText --- packages/frames-validator/src/validation.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index aaa7b3519..f8668bf54 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -75,7 +75,8 @@ async function checkUntrustedData( buttonIndex, opaqueConversationIdentifier, timestamp, - state, + state = "", + inputText = "", }: UntrustedData, actionBody: frames.FrameActionBody, ) { @@ -100,4 +101,8 @@ async function checkUntrustedData( if (actionBody.state !== state) { throw new Error("Mismatched state") } + + if (actionBody.inputText !== inputText) { + throw new Error("Missing input text") + } } From ce681fb2ea1dd0a244124976a560520ee21c63a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Apr 2024 21:43:27 +0000 Subject: [PATCH 35/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.6.0 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index 661060213..b9883cb2c 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.6.0 + +### Minor Changes + +- [#191](https://github.com/xmtp/xmtp-node-js-tools/pull/191) [`da721b9`](https://github.com/xmtp/xmtp-node-js-tools/commit/da721b981ba7b225345c7086952f343592796992) Thanks [@alexrisch](https://github.com/alexrisch)! - Added State handling + ## 0.5.2 ### Patch Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 2e43d44b9..215e085cf 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.5.2", + "version": "0.6.0", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 14834ba7751a5e959eb4795119d67002a2a890ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:29:14 +0000 Subject: [PATCH 36/51] build(deps): Bump @xmtp/proto from 3.45.0 to 3.52.0 Bumps [@xmtp/proto](https://github.com/xmtp/proto) from 3.45.0 to 3.52.0. - [Release notes](https://github.com/xmtp/proto/releases) - [Commits](https://github.com/xmtp/proto/compare/v3.45.0...v3.52.0) --- updated-dependencies: - dependency-name: "@xmtp/proto" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 215e085cf..df9f68aaf 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -26,7 +26,7 @@ "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.4.0", - "@xmtp/proto": "3.45.0", + "@xmtp/proto": "3.52.0", "viem": "^2.7.14" }, "scripts": { From 99ed2e064034ab172b361fabdcff0f9b6e7d1725 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:36:21 +0000 Subject: [PATCH 37/51] build(deps): Bump viem from 2.7.14 to 2.9.28 Bumps [viem](https://github.com/wevm/viem) from 2.7.14 to 2.9.28. - [Release notes](https://github.com/wevm/viem/releases) - [Commits](https://github.com/wevm/viem/compare/viem@2.7.14...viem@2.9.28) --- updated-dependencies: - dependency-name: viem dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index df9f68aaf..8075609a8 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -27,7 +27,7 @@ "@noble/curves": "^1.3.0", "@noble/hashes": "^1.4.0", "@xmtp/proto": "3.52.0", - "viem": "^2.7.14" + "viem": "^2.9.28" }, "scripts": { "clean": "rm -rf dist", From f542d8d92e7e25906c88ac679a3f438c6e4a92bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:57:20 +0000 Subject: [PATCH 38/51] build(deps): Bump @xmtp/proto from 3.52.0 to 3.56.0 Bumps [@xmtp/proto](https://github.com/xmtp/proto) from 3.52.0 to 3.56.0. - [Release notes](https://github.com/xmtp/proto/releases) - [Commits](https://github.com/xmtp/proto/compare/v3.52.0...v3.56.0) --- updated-dependencies: - dependency-name: "@xmtp/proto" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 8075609a8..ee206671d 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -26,7 +26,7 @@ "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.4.0", - "@xmtp/proto": "3.52.0", + "@xmtp/proto": "3.56.0", "viem": "^2.9.28" }, "scripts": { From 27a01c6fd70cacb11e7da12e7935ac8b0de34e5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 14:58:40 +0000 Subject: [PATCH 39/51] build(deps-dev): Bump typescript from 5.3.3 to 5.4.5 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.3.3 to 5.4.5. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.5) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index ee206671d..b22e3215e 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -50,7 +50,7 @@ "ethers": "^6.10.0", "rollup": "^4.13.0", "rollup-plugin-dts": "^6.1.0", - "typescript": "^5.3.3", + "typescript": "^5.4.5", "vitest": "^1.0.1" } } From 09fe63b33562ed3e27fe1458e1831c7e79955e96 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 May 2024 22:10:52 +0000 Subject: [PATCH 40/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.6.1 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index b9883cb2c..ecfceca7c 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.6.1 + +### Patch Changes + +- [#232](https://github.com/xmtp/xmtp-node-js-tools/pull/232) [`15c5032`](https://github.com/xmtp/xmtp-node-js-tools/commit/15c50320b06a80e50d666fa36da201cc754d3d68) Thanks [@daria-github](https://github.com/daria-github)! - Bumped version of proto package. + ## 0.6.0 ### Minor Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index b22e3215e..a237ceb54 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.6.0", + "version": "0.6.1", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From 8d7d2ca4c7c760e38ce1195d9cc7a244b391b787 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 14:19:24 +0000 Subject: [PATCH 41/51] build(deps): Bump @xmtp/proto from 3.56.0 to 3.61.1 Bumps [@xmtp/proto](https://github.com/xmtp/proto) from 3.56.0 to 3.61.1. - [Release notes](https://github.com/xmtp/proto/releases) - [Commits](https://github.com/xmtp/proto/compare/v3.56.0...v3.61.1) --- updated-dependencies: - dependency-name: "@xmtp/proto" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index a237ceb54..e86f3e3e2 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -26,7 +26,7 @@ "dependencies": { "@noble/curves": "^1.3.0", "@noble/hashes": "^1.4.0", - "@xmtp/proto": "3.56.0", + "@xmtp/proto": "3.61.1", "viem": "^2.9.28" }, "scripts": { From 9b43eef7a47af14351784c0bc41a259f529f9492 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:03:13 +0000 Subject: [PATCH 42/51] build(deps): Bump viem from 2.9.28 to 2.16.5 Bumps [viem](https://github.com/wevm/viem) from 2.9.28 to 2.16.5. - [Release notes](https://github.com/wevm/viem/releases) - [Commits](https://github.com/wevm/viem/compare/viem@2.9.28...viem@2.16.5) --- updated-dependencies: - dependency-name: viem dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index e86f3e3e2..90d95ddd8 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -27,7 +27,7 @@ "@noble/curves": "^1.3.0", "@noble/hashes": "^1.4.0", "@xmtp/proto": "3.61.1", - "viem": "^2.9.28" + "viem": "^2.16.5" }, "scripts": { "clean": "rm -rf dist", From f86e0d53101eff389f357cf2e3b19bf743f51a36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 16:17:14 +0000 Subject: [PATCH 43/51] build(deps-dev): Bump rollup-plugin-dts from 6.1.0 to 6.1.1 Bumps [rollup-plugin-dts](https://github.com/Swatinem/rollup-plugin-dts) from 6.1.0 to 6.1.1. - [Changelog](https://github.com/Swatinem/rollup-plugin-dts/blob/master/CHANGELOG.md) - [Commits](https://github.com/Swatinem/rollup-plugin-dts/compare/v6.1.0...v6.1.1) --- updated-dependencies: - dependency-name: rollup-plugin-dts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/frames-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 90d95ddd8..99d26a6db 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -49,7 +49,7 @@ "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", "rollup": "^4.13.0", - "rollup-plugin-dts": "^6.1.0", + "rollup-plugin-dts": "^6.1.1", "typescript": "^5.4.5", "vitest": "^1.0.1" } From 8e672eedd1c837d8440f3d1edbe87bbe2f314b18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Jul 2024 09:58:10 +0000 Subject: [PATCH 44/51] RELEASING: Releasing 1 package(s) Releases: @xmtp/frames-validator@0.6.2 [skip ci] --- packages/frames-validator/CHANGELOG.md | 6 ++++++ packages/frames-validator/package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frames-validator/CHANGELOG.md b/packages/frames-validator/CHANGELOG.md index ecfceca7c..5379c5322 100644 --- a/packages/frames-validator/CHANGELOG.md +++ b/packages/frames-validator/CHANGELOG.md @@ -1,5 +1,11 @@ # @xmtp/frames-validator +## 0.6.2 + +### Patch Changes + +- [#260](https://github.com/xmtp/xmtp-node-js-tools/pull/260) [`57bf55d`](https://github.com/xmtp/xmtp-node-js-tools/commit/57bf55d89bce8a52a1dfaf8b7fc649054aaa6fd5) Thanks [@neekolas](https://github.com/neekolas)! - Update dependencies + ## 0.6.1 ### Patch Changes diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 99d26a6db..f7a0e89dd 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/frames-validator", - "version": "0.6.1", + "version": "0.6.2", "description": "A validator for XMTP frames requests", "type": "module", "main": "dist/index.cjs", From e06f8c733711ed8668bd519d251d3f4f141cdb2a Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Thu, 17 Oct 2024 17:13:47 -0500 Subject: [PATCH 45/51] Update tsconfig --- packages/frames-validator/tsconfig.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/frames-validator/tsconfig.json b/packages/frames-validator/tsconfig.json index f4bb6b84f..016c95fe6 100644 --- a/packages/frames-validator/tsconfig.json +++ b/packages/frames-validator/tsconfig.json @@ -1,8 +1,4 @@ { - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist/esm", - "composite": false - }, - "includes": ["src/**/*"] + "extends": "tsconfig/base.json", + "includes": ["src", "rollup.config.js", "vitest.config.ts", "vitest.setup.ts"] } From a02f9789ff6dcc2f4c14c365b42f999b478da9ea Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Thu, 17 Oct 2024 17:14:15 -0500 Subject: [PATCH 46/51] Run prettier --- packages/frames-validator/package.json | 44 +++++----- packages/frames-validator/rollup.config.js | 12 +-- packages/frames-validator/src/index.test.ts | 85 +++++++++---------- packages/frames-validator/src/index.ts | 4 +- packages/frames-validator/src/openFrames.ts | 33 ++++---- packages/frames-validator/src/types.ts | 24 +++--- packages/frames-validator/src/utils.ts | 94 +++++++++++---------- packages/frames-validator/src/validation.ts | 58 ++++++------- packages/frames-validator/vitest.config.ts | 10 +-- packages/frames-validator/vitest.setup.ts | 4 +- 10 files changed, 184 insertions(+), 184 deletions(-) diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index f7a0e89dd..70b28270a 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -2,10 +2,13 @@ "name": "@xmtp/frames-validator", "version": "0.6.2", "description": "A validator for XMTP frames requests", + "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", + "bugs": { + "url": "https://github.com/xmtp/xmtp-node-js-tools/issues" + }, + "license": "MIT", + "author": "XMTP Labs ", "type": "module", - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", @@ -13,15 +16,17 @@ "require": "./dist/index.cjs" } }, - "engines": { - "node": ">=18" - }, + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", "files": [ "dist" ], - "publishConfig": { - "access": "public", - "provenance": true + "scripts": { + "build": "yarn clean && rollup -c", + "clean": "rm -rf dist", + "prepublishOnly": "yarn build", + "test": "vitest run ./src" }, "dependencies": { "@noble/curves": "^1.3.0", @@ -29,19 +34,6 @@ "@xmtp/proto": "3.61.1", "viem": "^2.16.5" }, - "scripts": { - "clean": "rm -rf dist", - "build": "yarn clean && rollup -c", - "prepublishOnly": "yarn build", - "test": "vitest run ./src" - }, - "author": "XMTP Labs ", - "license": "MIT", - "bugs": { - "url": "https://github.com/xmtp/xmtp-node-js-tools/issues" - }, - "homepage": "https://github.com/xmtp/xmtp-node-js-tools#readme", - "packageManager": "yarn@4.0.0", "devDependencies": { "@open-frames/types": "^0.0.7", "@rollup/plugin-typescript": "^11.1.6", @@ -52,5 +44,13 @@ "rollup-plugin-dts": "^6.1.1", "typescript": "^5.4.5", "vitest": "^1.0.1" + }, + "packageManager": "yarn@4.0.0", + "engines": { + "node": ">=18" + }, + "publishConfig": { + "access": "public", + "provenance": true } } diff --git a/packages/frames-validator/rollup.config.js b/packages/frames-validator/rollup.config.js index 6fbea6ab4..240d93532 100644 --- a/packages/frames-validator/rollup.config.js +++ b/packages/frames-validator/rollup.config.js @@ -1,15 +1,15 @@ -import typescript from "@rollup/plugin-typescript" -import { defineConfig } from "rollup" -import { dts } from "rollup-plugin-dts" +import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from "rollup"; +import { dts } from "rollup-plugin-dts"; -const external = ["@xmtp/proto", "node:crypto", "@xmtp/xmtp-js", "long"] +const external = ["@xmtp/proto", "node:crypto", "@xmtp/xmtp-js", "long"]; const plugins = [ typescript({ declaration: false, declarationMap: false, }), -] +]; export default defineConfig([ { @@ -40,4 +40,4 @@ export default defineConfig([ }, plugins: [dts()], }, -]) +]); diff --git a/packages/frames-validator/src/index.test.ts b/packages/frames-validator/src/index.test.ts index f8fd3f8e3..6ae6b2123 100644 --- a/packages/frames-validator/src/index.test.ts +++ b/packages/frames-validator/src/index.test.ts @@ -1,45 +1,44 @@ -import { FramesClient } from "@xmtp/frames-client" -import { fetcher, frames } from "@xmtp/proto" -import { Client, PrivateKeyBundleV2 } from "@xmtp/xmtp-js" -import { Wallet } from "ethers" -import { beforeEach, describe, expect, it } from "vitest" +import { FramesClient } from "@xmtp/frames-client"; +import { fetcher, frames } from "@xmtp/proto"; +import { Client, PrivateKeyBundleV2 } from "@xmtp/xmtp-js"; +import { Wallet } from "ethers"; +import { beforeEach, describe, expect, it } from "vitest"; +import { deserializeProtoMessage, validateFramesPost } from "."; -import { deserializeProtoMessage, validateFramesPost } from "." - -const { b64Decode, b64Encode } = fetcher +const { b64Decode, b64Encode } = fetcher; function scrambleBytes(bytes: Uint8Array) { - const scrambled = new Uint8Array(bytes.length) + const scrambled = new Uint8Array(bytes.length); for (let i = 0; i < bytes.length; i++) { - scrambled[i] = bytes[bytes.length - i - 1] + scrambled[i] = bytes[bytes.length - i - 1]; } - return scrambled + return scrambled; } describe("validations", () => { - let client: Client - let framesClient: FramesClient + let client: Client; + let framesClient: FramesClient; - const FRAME_URL = "https://frame.xyz" - const CONVERSATION_TOPIC = "/xmtp/0/1234" - const PARTICIPANT_ACCOUNT_ADDRESSES = ["0x1234", "0x5678"] - const BUTTON_INDEX = 2 + const FRAME_URL = "https://frame.xyz"; + const CONVERSATION_TOPIC = "/xmtp/0/1234"; + const PARTICIPANT_ACCOUNT_ADDRESSES = ["0x1234", "0x5678"]; + const BUTTON_INDEX = 2; beforeEach(async () => { - const wallet = Wallet.createRandom() - client = await Client.create(wallet) - framesClient = new FramesClient(client) - }) + const wallet = Wallet.createRandom(); + client = await Client.create(wallet); + framesClient = new FramesClient(client); + }); it("succeeds in the happy path", async () => { const postData = await framesClient.signFrameAction({ buttonIndex: BUTTON_INDEX, frameUrl: FRAME_URL, conversationTopic: CONVERSATION_TOPIC, participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, - }) - const validated = await validateFramesPost(postData) - expect(validated.verifiedWalletAddress).toEqual(client.address) - }) + }); + const validated = await validateFramesPost(postData); + expect(validated.verifiedWalletAddress).toEqual(client.address); + }); it("fails if the signature verification fails", async () => { const postData = await framesClient.signFrameAction({ @@ -47,33 +46,33 @@ describe("validations", () => { frameUrl: FRAME_URL, conversationTopic: CONVERSATION_TOPIC, participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, - }) + }); // Monkey around with the signature const deserialized = deserializeProtoMessage( b64Decode(postData.trustedData.messageBytes), - ) + ); if (!deserialized.signature?.ecdsaCompact?.bytes) { - throw new Error("Signature bytes are empty") + throw new Error("Signature bytes are empty"); } deserialized.signature.ecdsaCompact.bytes = scrambleBytes( deserialized.signature.ecdsaCompact?.bytes, - ) + ); const reserialized = frames.FrameAction.encode({ signature: deserialized.signature, actionBody: deserialized.actionBodyBytes, signedPublicKeyBundle: deserialized.signedPublicKeyBundle, - }).finish() + }).finish(); postData.trustedData.messageBytes = b64Encode( reserialized, 0, reserialized.length, - ) + ); - expect(validateFramesPost(postData)).rejects.toThrow() - }) + expect(validateFramesPost(postData)).rejects.toThrow(); + }); it("fails if the wallet address doesn't match", async () => { const postData = await framesClient.signFrameAction({ @@ -81,33 +80,33 @@ describe("validations", () => { frameUrl: FRAME_URL, conversationTopic: CONVERSATION_TOPIC, participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, - }) + }); // Monkey around with the signature const deserialized = deserializeProtoMessage( b64Decode(postData.trustedData.messageBytes), - ) + ); if (!deserialized.signedPublicKeyBundle) { - throw new Error("Public key bunlde is empty") + throw new Error("Public key bunlde is empty"); } - const throwAwayWallet = Wallet.createRandom() + const throwAwayWallet = Wallet.createRandom(); const wrongPublicKeyBundle = ( await PrivateKeyBundleV2.generate(throwAwayWallet) - ).getPublicKeyBundle() + ).getPublicKeyBundle(); const reserialized = frames.FrameAction.encode({ signature: deserialized.signature, actionBody: deserialized.actionBodyBytes, signedPublicKeyBundle: wrongPublicKeyBundle, - }).finish() + }).finish(); postData.trustedData.messageBytes = b64Encode( reserialized, 0, reserialized.length, - ) + ); - expect(validateFramesPost(postData)).rejects.toThrow() - }) -}) + expect(validateFramesPost(postData)).rejects.toThrow(); + }); +}); diff --git a/packages/frames-validator/src/index.ts b/packages/frames-validator/src/index.ts index a43697110..9248b3301 100644 --- a/packages/frames-validator/src/index.ts +++ b/packages/frames-validator/src/index.ts @@ -1,2 +1,2 @@ -export * from "./openFrames.js" -export * from "./validation.js" +export * from "./openFrames.js"; +export * from "./validation.js"; diff --git a/packages/frames-validator/src/openFrames.ts b/packages/frames-validator/src/openFrames.ts index acdda04c3..e768c150a 100644 --- a/packages/frames-validator/src/openFrames.ts +++ b/packages/frames-validator/src/openFrames.ts @@ -2,38 +2,37 @@ import { OpenFramesRequest, RequestValidator, ValidationResponse, -} from "@open-frames/types" - -import { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types" -import { validateFramesPost } from "./validation" +} from "@open-frames/types"; +import { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types"; +import { validateFramesPost } from "./validation"; export class XmtpValidator implements RequestValidator { - readonly protocolIdentifier = "xmtp" - readonly minProtocolVersionDate = "2024-02-09" + readonly protocolIdentifier = "xmtp"; + readonly minProtocolVersionDate = "2024-02-09"; minProtocolVersion(): string { - return `${this.protocolIdentifier}@${this.minProtocolVersionDate}` + return `${this.protocolIdentifier}@${this.minProtocolVersionDate}`; } isSupported(payload: OpenFramesRequest): payload is XmtpOpenFramesRequest { if (!payload.clientProtocol) { - return false + return false; } - const [protocol, version] = payload.clientProtocol.split("@") + const [protocol, version] = payload.clientProtocol.split("@"); if (!protocol || !version) { - return false + return false; } - const isCorrectClientProtocol = protocol === "xmtp" - const isCorrectVersion = version >= this.minProtocolVersionDate + const isCorrectClientProtocol = protocol === "xmtp"; + const isCorrectVersion = version >= this.minProtocolVersionDate; const isTrustedDataValid = - typeof payload.trustedData?.messageBytes === "string" + typeof payload.trustedData?.messageBytes === "string"; - return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid + return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid; } async validate( @@ -42,16 +41,16 @@ export class XmtpValidator ValidationResponse > { try { - const validationResponse = await validateFramesPost(payload) + const validationResponse = await validateFramesPost(payload); return { isValid: true, clientProtocol: payload.clientProtocol, message: validationResponse, - } + }; } catch (error) { return { isValid: false, - } + }; } } } diff --git a/packages/frames-validator/src/types.ts b/packages/frames-validator/src/types.ts index e4ac1a56b..e4b556fc9 100644 --- a/packages/frames-validator/src/types.ts +++ b/packages/frames-validator/src/types.ts @@ -1,21 +1,21 @@ import type { OpenFramesTrustedData, OpenFramesUntrustedData, -} from "@open-frames/types" -import { frames } from "@xmtp/proto" +} from "@open-frames/types"; +import { frames } from "@xmtp/proto"; export type UntrustedData = OpenFramesUntrustedData & { - walletAddress: string // Untrusted version of the wallet address - opaqueConversationIdentifier: string // A hash of the conversation topic and the participants -} + walletAddress: string; // Untrusted version of the wallet address + opaqueConversationIdentifier: string; // A hash of the conversation topic and the participants +}; export type XmtpOpenFramesRequest = { - clientProtocol: `xmtp@${string}` - untrustedData: UntrustedData - trustedData: OpenFramesTrustedData -} + clientProtocol: `xmtp@${string}`; + untrustedData: UntrustedData; + trustedData: OpenFramesTrustedData; +}; export type XmtpValidationResponse = { - actionBody: frames.FrameActionBody - verifiedWalletAddress: string -} + actionBody: frames.FrameActionBody; + verifiedWalletAddress: string; +}; diff --git a/packages/frames-validator/src/utils.ts b/packages/frames-validator/src/utils.ts index febcc51ce..df1cc422a 100644 --- a/packages/frames-validator/src/utils.ts +++ b/packages/frames-validator/src/utils.ts @@ -1,40 +1,40 @@ -import { bytesToHex } from "@noble/curves/abstract/utils" -import { secp256k1 } from "@noble/curves/secp256k1" -import { sha256 } from "@noble/hashes/sha256" -import { publicKey, signature } from "@xmtp/proto" -import { SignedPublicKeyBundle } from "@xmtp/xmtp-js" +import { bytesToHex } from "@noble/curves/abstract/utils"; +import { secp256k1 } from "@noble/curves/secp256k1"; +import { sha256 } from "@noble/hashes/sha256"; +import { publicKey, signature } from "@xmtp/proto"; +import { SignedPublicKeyBundle } from "@xmtp/xmtp-js"; import { - bytesToHex as viemBytesToHex, getAddress, hashMessage, keccak256, -} from "viem/utils" + bytesToHex as viemBytesToHex, +} from "viem/utils"; export type ECDSACompactWithRecovery = { - bytes: Uint8Array // compact representation [ R || S ], 64 bytes - recovery: number // recovery bit -} + bytes: Uint8Array; // compact representation [ R || S ], 64 bytes + recovery: number; // recovery bit +}; // hexToBytes implementation that is compatible with `xmtp-js`'s implementation export function hexToBytes(s: string): Uint8Array { if (s.startsWith("0x")) { - s = s.slice(2) + s = s.slice(2); } - const bytes = new Uint8Array(s.length / 2) + const bytes = new Uint8Array(s.length / 2); for (let i = 0; i < bytes.length; i++) { - const j = i * 2 - bytes[i] = Number.parseInt(s.slice(j, j + 2), 16) + const j = i * 2; + bytes[i] = Number.parseInt(s.slice(j, j + 2), 16); } - return bytes + return bytes; } // Ensure the signature is valid function ecdsaCheck(sig: ECDSACompactWithRecovery): void { if (sig.bytes.length !== 64) { - throw new Error(`invalid signature length: ${sig.bytes.length}`) + throw new Error(`invalid signature length: ${sig.bytes.length}`); } if (sig.recovery !== 0 && sig.recovery !== 1) { - throw new Error(`invalid recovery bit: ${sig.recovery}`) + throw new Error(`invalid recovery bit: ${sig.recovery}`); } } @@ -44,13 +44,13 @@ function extractSignature( signature: signature.Signature, ): ECDSACompactWithRecovery { if (signature.ecdsaCompact?.bytes) { - ecdsaCheck(signature.ecdsaCompact) - return signature.ecdsaCompact + ecdsaCheck(signature.ecdsaCompact); + return signature.ecdsaCompact; } else if (signature.walletEcdsaCompact?.bytes) { - ecdsaCheck(signature.walletEcdsaCompact) - return signature.walletEcdsaCompact + ecdsaCheck(signature.walletEcdsaCompact); + return signature.walletEcdsaCompact; } else { - throw new Error("invalid signature") + throw new Error("invalid signature"); } } @@ -61,7 +61,7 @@ function walletSignatureText(keyBytes: Uint8Array): string { `${bytesToHex(keyBytes)}\n` + "\n" + "For more info: https://xmtp.org/signatures/" - ) + ); } // Ensure that the `SignedPublicKeyBundle` has the required fields @@ -69,12 +69,12 @@ function validateSignedPublicKeyBundle( bundle: publicKey.SignedPublicKeyBundle, ): bundle is SignedPublicKeyBundle { if (!bundle.identityKey?.keyBytes) { - return false + return false; } if (!bundle.preKey?.keyBytes) { - return false + return false; } - return true + return true; } /** @@ -89,45 +89,47 @@ export function verifyIdentityKeySignature( bundle: publicKey.SignedPublicKeyBundle, ) { if (!validateSignedPublicKeyBundle(bundle)) { - throw new Error("Invalid public key bundle") + throw new Error("Invalid public key bundle"); } if (!sig.ecdsaCompact?.bytes) { - throw new Error("Missing ECDSA compact") + throw new Error("Missing ECDSA compact"); } - const pubKey = publicKey.UnsignedPublicKey.decode(bundle.identityKey.keyBytes) + const pubKey = publicKey.UnsignedPublicKey.decode( + bundle.identityKey.keyBytes, + ); if (!pubKey.secp256k1Uncompressed?.bytes) { - throw new Error("Missing key bytes") + throw new Error("Missing key bytes"); } - const digest = sha256(message) + const digest = sha256(message); const isVerified = secp256k1.verify( sig.ecdsaCompact.bytes, digest, pubKey.secp256k1Uncompressed.bytes, - ) + ); if (!isVerified) { - throw new Error("Invalid signature") + throw new Error("Invalid signature"); } } function computeAddress(bytes: Uint8Array) { - const publicKey = viemBytesToHex(bytes.slice(1)) as `0x${string}` - const hash = keccak256(publicKey) - const address = hash.substring(hash.length - 40) - return getAddress(`0x${address}`) + const publicKey = viemBytesToHex(bytes.slice(1)) as `0x${string}`; + const hash = keccak256(publicKey); + const address = hash.substring(hash.length - 40); + return getAddress(`0x${address}`); } function recoverWalletAddress( messageString: string, sig: ECDSACompactWithRecovery, ) { - const digest = hexToBytes(hashMessage(messageString)) + const digest = hexToBytes(hashMessage(messageString)); const pubKey = secp256k1.Signature.fromCompact(sig.bytes) .addRecoveryBit(sig.recovery) .recoverPublicKey(digest) - .toRawBytes(false) + .toRawBytes(false); - return computeAddress(pubKey) + return computeAddress(pubKey); } /** @@ -139,19 +141,19 @@ export function verifyWalletSignature( publicKeyBundle: publicKey.SignedPublicKeyBundle, ) { if (!validateSignedPublicKeyBundle(publicKeyBundle)) { - throw new Error("Invalid public key bundle") + throw new Error("Invalid public key bundle"); } - const toVerify = extractSignature(publicKeyBundle.identityKey.signature) + const toVerify = extractSignature(publicKeyBundle.identityKey.signature); const signatureText = walletSignatureText( publicKeyBundle.identityKey.keyBytes, - ) + ); - const walletAddress = recoverWalletAddress(signatureText, toVerify) + const walletAddress = recoverWalletAddress(signatureText, toVerify); if (!walletAddress) { - throw new Error("Could not recover wallet address") + throw new Error("Could not recover wallet address"); } - return walletAddress + return walletAddress; } diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index f8668bf54..a26a6b6b1 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -1,61 +1,61 @@ -import { fetcher, frames, publicKey, signature } from "@xmtp/proto" - +import { fetcher, frames, publicKey, signature } from "@xmtp/proto"; import { UntrustedData, XmtpOpenFramesRequest, XmtpValidationResponse, -} from "./types.js" -import { verifyIdentityKeySignature, verifyWalletSignature } from "./utils.js" -export * from "./types.js" +} from "./types.js"; +import { verifyIdentityKeySignature, verifyWalletSignature } from "./utils.js"; + +export * from "./types.js"; -const { b64Decode } = fetcher +const { b64Decode } = fetcher; export async function validateFramesPost( data: XmtpOpenFramesRequest, ): Promise { - const { untrustedData, trustedData } = data - const { walletAddress } = untrustedData - const { messageBytes: messageBytesString } = trustedData + const { untrustedData, trustedData } = data; + const { walletAddress } = untrustedData; + const { messageBytes: messageBytesString } = trustedData; - const messageBytes = b64Decode(messageBytesString) + const messageBytes = b64Decode(messageBytesString); const { actionBody, actionBodyBytes, signature, signedPublicKeyBundle } = - deserializeProtoMessage(messageBytes) + deserializeProtoMessage(messageBytes); const verifiedWalletAddress = await getVerifiedWalletAddress( actionBodyBytes, signature, signedPublicKeyBundle, - ) + ); if (verifiedWalletAddress !== walletAddress) { - console.log(`${verifiedWalletAddress} !== ${walletAddress}`) - throw new Error("Invalid wallet address") + console.log(`${verifiedWalletAddress} !== ${walletAddress}`); + throw new Error("Invalid wallet address"); } - await checkUntrustedData(untrustedData, actionBody) + await checkUntrustedData(untrustedData, actionBody); return { actionBody, verifiedWalletAddress, - } + }; } export function deserializeProtoMessage(messageBytes: Uint8Array) { - const frameAction = frames.FrameAction.decode(messageBytes) + const frameAction = frames.FrameAction.decode(messageBytes); if (!frameAction.signature || !frameAction.signedPublicKeyBundle) { throw new Error( "Invalid frame action: missing signature or signed public key bundle", - ) + ); } - const actionBody = frames.FrameActionBody.decode(frameAction.actionBody) + const actionBody = frames.FrameActionBody.decode(frameAction.actionBody); return { actionBody, actionBodyBytes: frameAction.actionBody, signature: frameAction.signature, signedPublicKeyBundle: frameAction.signedPublicKeyBundle, - } + }; } async function getVerifiedWalletAddress( @@ -63,10 +63,10 @@ async function getVerifiedWalletAddress( signature: signature.Signature, signedPublicKeyBundle: publicKey.SignedPublicKeyBundle, ): Promise { - const walletAddress = verifyWalletSignature(signedPublicKeyBundle) - verifyIdentityKeySignature(actionBodyBytes, signature, signedPublicKeyBundle) + const walletAddress = verifyWalletSignature(signedPublicKeyBundle); + verifyIdentityKeySignature(actionBodyBytes, signature, signedPublicKeyBundle); - return walletAddress + return walletAddress; } async function checkUntrustedData( @@ -81,28 +81,28 @@ async function checkUntrustedData( actionBody: frames.FrameActionBody, ) { if (actionBody.frameUrl !== url) { - throw new Error("Mismatched URL") + throw new Error("Mismatched URL"); } if (actionBody.buttonIndex !== buttonIndex) { - throw new Error("Mismatched button index") + throw new Error("Mismatched button index"); } if ( actionBody.opaqueConversationIdentifier !== opaqueConversationIdentifier ) { - throw new Error("Mismatched conversation identifier") + throw new Error("Mismatched conversation identifier"); } if (actionBody.timestamp.toNumber() !== timestamp) { - throw new Error("Mismatched timestamp") + throw new Error("Mismatched timestamp"); } if (actionBody.state !== state) { - throw new Error("Mismatched state") + throw new Error("Mismatched state"); } if (actionBody.inputText !== inputText) { - throw new Error("Missing input text") + throw new Error("Missing input text"); } } diff --git a/packages/frames-validator/vitest.config.ts b/packages/frames-validator/vitest.config.ts index a1ab5a8b2..3901d2a6f 100644 --- a/packages/frames-validator/vitest.config.ts +++ b/packages/frames-validator/vitest.config.ts @@ -1,10 +1,10 @@ -import { defineConfig, mergeConfig } from "vite" -import { defineConfig as defineVitestConfig } from "vitest/config" +import { defineConfig, mergeConfig } from "vite"; +import { defineConfig as defineVitestConfig } from "vitest/config"; // https://vitejs.dev/config/ const viteConfig = defineConfig({ plugins: [], -}) +}); const vitestConfig = defineVitestConfig({ test: { @@ -12,6 +12,6 @@ const vitestConfig = defineVitestConfig({ environment: "node", setupFiles: "./vitest.setup.ts", }, -}) +}); -export default mergeConfig(viteConfig, vitestConfig) +export default mergeConfig(viteConfig, vitestConfig); diff --git a/packages/frames-validator/vitest.setup.ts b/packages/frames-validator/vitest.setup.ts index 7c6dd095f..c0ab99828 100644 --- a/packages/frames-validator/vitest.setup.ts +++ b/packages/frames-validator/vitest.setup.ts @@ -1,5 +1,5 @@ -import { webcrypto } from "crypto" +import { webcrypto } from "crypto"; // eslint-disable-next-line // @ts-ignore -global.crypto = webcrypto +global.crypto = webcrypto; From 83431638765fbb4ba9fa1fd13b065da1c10902b6 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Thu, 17 Oct 2024 17:17:15 -0500 Subject: [PATCH 47/51] Update dev deps --- .../content-type-primitives/package.json | 2 +- .../content-type-reaction/package.json | 2 +- .../content-type-read-receipt/package.json | 2 +- .../package.json | 2 +- content-types/content-type-reply/package.json | 2 +- content-types/content-type-text/package.json | 2 +- .../package.json | 2 +- package.json | 2 +- packages/frames-validator/package.json | 14 +-- sdks/js-sdk/package.json | 2 +- sdks/node-sdk/package.json | 2 +- yarn.lock | 107 +++++++++++++++--- 12 files changed, 109 insertions(+), 32 deletions(-) diff --git a/content-types/content-type-primitives/package.json b/content-types/content-type-primitives/package.json index 4c3f47aa6..85e74906d 100644 --- a/content-types/content-type-primitives/package.json +++ b/content-types/content-type-primitives/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "happy-dom": "^15.7.4", "rimraf": "^6.0.1", "rollup": "^4.24.0", diff --git a/content-types/content-type-reaction/package.json b/content-types/content-type-reaction/package.json index 3eedda353..ecaddd25d 100644 --- a/content-types/content-type-reaction/package.json +++ b/content-types/content-type-reaction/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", "ethers": "^6.11.1", diff --git a/content-types/content-type-read-receipt/package.json b/content-types/content-type-read-receipt/package.json index 78df7fdd9..23fe614d8 100644 --- a/content-types/content-type-read-receipt/package.json +++ b/content-types/content-type-read-receipt/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", "ethers": "^6.11.1", diff --git a/content-types/content-type-remote-attachment/package.json b/content-types/content-type-remote-attachment/package.json index 11de444c8..c27e7b185 100644 --- a/content-types/content-type-remote-attachment/package.json +++ b/content-types/content-type-remote-attachment/package.json @@ -70,7 +70,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/rollup-plugin-resolve-extensions": "^1.0.1", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", diff --git a/content-types/content-type-reply/package.json b/content-types/content-type-reply/package.json index 132bc7712..cc667141a 100644 --- a/content-types/content-type-reply/package.json +++ b/content-types/content-type-reply/package.json @@ -69,7 +69,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/content-type-remote-attachment": "workspace:*", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", diff --git a/content-types/content-type-text/package.json b/content-types/content-type-text/package.json index 088540983..3678d580d 100644 --- a/content-types/content-type-text/package.json +++ b/content-types/content-type-text/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", "ethers": "^6.11.1", diff --git a/content-types/content-type-transaction-reference/package.json b/content-types/content-type-transaction-reference/package.json index eb04bb628..38aa75a37 100644 --- a/content-types/content-type-transaction-reference/package.json +++ b/content-types/content-type-transaction-reference/package.json @@ -68,7 +68,7 @@ "devDependencies": { "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@xmtp/xmtp-js": "^11.6.3", "buffer": "^6.0.3", "ethers": "^6.11.1", diff --git a/package.json b/package.json index 700d6a4df..e5a3937aa 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@eslint/js": "^9.12.0", "@ianvs/prettier-plugin-sort-imports": "^4.3.1", "@types/eslint__js": "^8.42.3", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "eslint": "^9.12.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 70b28270a..06e3af906 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -35,17 +35,17 @@ "viem": "^2.16.5" }, "devDependencies": { - "@open-frames/types": "^0.0.7", - "@rollup/plugin-typescript": "^11.1.6", - "@xmtp/frames-client": "^0.4.3", + "@open-frames/types": "^0.1.1", + "@rollup/plugin-typescript": "^12.1.1", + "@xmtp/frames-client": "^0.5.4", "@xmtp/xmtp-js": "^11.3.12", "ethers": "^6.10.0", - "rollup": "^4.13.0", + "rollup": "^4.24.0", "rollup-plugin-dts": "^6.1.1", - "typescript": "^5.4.5", - "vitest": "^1.0.1" + "typescript": "^5.6.3", + "vitest": "^2.1.3" }, - "packageManager": "yarn@4.0.0", + "packageManager": "yarn@4.5.0", "engines": { "node": ">=18" }, diff --git a/sdks/js-sdk/package.json b/sdks/js-sdk/package.json index 69c1fe080..393d004c3 100644 --- a/sdks/js-sdk/package.json +++ b/sdks/js-sdk/package.json @@ -113,7 +113,7 @@ "@types/bl": "^5.1.0", "@types/callback-to-async-iterator": "^1.1.7", "@types/elliptic": "^6.4.18", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@vitest/coverage-v8": "^2.1.3", "@xmtp/rollup-plugin-resolve-extensions": "1.0.1", "benny": "^3.7.1", diff --git a/sdks/node-sdk/package.json b/sdks/node-sdk/package.json index 95ce99c5f..48dd508b2 100644 --- a/sdks/node-sdk/package.json +++ b/sdks/node-sdk/package.json @@ -56,7 +56,7 @@ "devDependencies": { "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-typescript": "^12.1.1", - "@types/node": "^20.16.11", + "@types/node": "^20.16.12", "@vitest/coverage-v8": "^2.1.3", "@xmtp/xmtp-js": "workspace:^", "fast-glob": "^3.3.2", diff --git a/yarn.lock b/yarn.lock index f82b15f82..4be6d5631 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1511,7 +1511,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:~1.6.0": +"@noble/curves@npm:1.6.0, @noble/curves@npm:^1.3.0, @noble/curves@npm:^1.4.0, @noble/curves@npm:~1.6.0": version: 1.6.0 resolution: "@noble/curves@npm:1.6.0" dependencies: @@ -2286,7 +2286,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.16.11": +"@types/node@npm:^20.16.12": version: 20.16.12 resolution: "@types/node@npm:20.16.12" dependencies: @@ -2569,7 +2569,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/proto": "npm:^3.61.1" happy-dom: "npm:^15.7.4" rimraf: "npm:^6.0.1" @@ -2588,7 +2588,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.2" "@xmtp/xmtp-js": "npm:^11.6.3" buffer: "npm:^6.0.3" @@ -2610,7 +2610,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.2" "@xmtp/xmtp-js": "npm:^11.6.3" buffer: "npm:^6.0.3" @@ -2633,7 +2633,7 @@ __metadata: "@noble/secp256k1": "npm:^1.7.1" "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.2" "@xmtp/proto": "npm:^3.61.1" "@xmtp/rollup-plugin-resolve-extensions": "npm:^1.0.1" @@ -2657,7 +2657,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.2" "@xmtp/content-type-remote-attachment": "workspace:*" "@xmtp/proto": "npm:^3.61.1" @@ -2681,7 +2681,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.1" "@xmtp/xmtp-js": "npm:^11.6.3" buffer: "npm:^6.0.3" @@ -2703,7 +2703,7 @@ __metadata: dependencies: "@rollup/plugin-terser": "npm:^0.4.4" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@xmtp/content-type-primitives": "npm:^1.0.1" "@xmtp/xmtp-js": "npm:^11.6.3" buffer: "npm:^6.0.3" @@ -2719,7 +2719,30 @@ __metadata: languageName: unknown linkType: soft -"@xmtp/frames-client@workspace:packages/frames-client": +"@xmtp/encryption@workspace:shared/encryption": + version: 0.0.0-use.local + resolution: "@xmtp/encryption@workspace:shared/encryption" + dependencies: + "@rollup/plugin-terser": "npm:^0.4.4" + "@rollup/plugin-typescript": "npm:^12.1.1" + "@types/node": "npm:^20.16.12" + "@vitest/coverage-v8": "npm:^2.1.3" + "@xmtp/proto": "npm:^3.68.0" + "@xmtp/rollup-plugin-resolve-extensions": "npm:1.0.1" + happy-dom: "npm:^15.7.4" + rimraf: "npm:^6.0.1" + rollup: "npm:^4.24.0" + rollup-plugin-dts: "npm:^6.1.1" + rollup-plugin-filesize: "npm:^10.0.0" + rollup-plugin-tsconfig-paths: "npm:^1.5.2" + typescript: "npm:^5.6.3" + vite: "npm:^5.4.9" + vite-tsconfig-paths: "npm:^5.0.1" + vitest: "npm:^2.1.3" + languageName: unknown + linkType: soft + +"@xmtp/frames-client@npm:^0.5.4, @xmtp/frames-client@workspace:packages/frames-client": version: 0.0.0-use.local resolution: "@xmtp/frames-client@workspace:packages/frames-client" dependencies: @@ -2747,6 +2770,26 @@ __metadata: languageName: unknown linkType: soft +"@xmtp/frames-validator@workspace:packages/frames-validator": + version: 0.0.0-use.local + resolution: "@xmtp/frames-validator@workspace:packages/frames-validator" + dependencies: + "@noble/curves": "npm:^1.3.0" + "@noble/hashes": "npm:^1.4.0" + "@open-frames/types": "npm:^0.1.1" + "@rollup/plugin-typescript": "npm:^12.1.1" + "@xmtp/frames-client": "npm:^0.5.4" + "@xmtp/proto": "npm:3.61.1" + "@xmtp/xmtp-js": "npm:^11.3.12" + ethers: "npm:^6.10.0" + rollup: "npm:^4.24.0" + rollup-plugin-dts: "npm:^6.1.1" + typescript: "npm:^5.6.3" + viem: "npm:^2.16.5" + vitest: "npm:^2.1.3" + languageName: unknown + linkType: soft + "@xmtp/node-bindings@npm:^0.0.13": version: 0.0.13 resolution: "@xmtp/node-bindings@npm:0.0.13" @@ -2760,7 +2803,7 @@ __metadata: dependencies: "@rollup/plugin-json": "npm:^6.1.0" "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@vitest/coverage-v8": "npm:^2.1.3" "@xmtp/content-type-primitives": "npm:^1.0.1" "@xmtp/content-type-text": "npm:^1.0.0" @@ -2793,6 +2836,18 @@ __metadata: languageName: node linkType: hard +"@xmtp/proto@npm:3.61.1": + version: 3.61.1 + resolution: "@xmtp/proto@npm:3.61.1" + dependencies: + long: "npm:^5.2.0" + protobufjs: "npm:^7.0.0" + rxjs: "npm:^7.8.0" + undici: "npm:^5.8.1" + checksum: 10/c5acae46ad301a50652f30384be55a3389b4c11994652fa5386052c7ff4111fcb15c0e9d267898d6173cbcb6559b24bd7bef7470f020388f8610fecd3b8deea9 + languageName: node + linkType: hard + "@xmtp/proto@npm:3.62.1": version: 3.62.1 resolution: "@xmtp/proto@npm:3.62.1" @@ -2837,7 +2892,7 @@ __metadata: languageName: node linkType: hard -"@xmtp/xmtp-js@npm:^11.6.3": +"@xmtp/xmtp-js@npm:^11.3.12, @xmtp/xmtp-js@npm:^11.6.3": version: 11.6.3 resolution: "@xmtp/xmtp-js@npm:11.6.3" dependencies: @@ -2884,7 +2939,7 @@ __metadata: "@types/bl": "npm:^5.1.0" "@types/callback-to-async-iterator": "npm:^1.1.7" "@types/elliptic": "npm:^6.4.18" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" "@vitest/coverage-v8": "npm:^2.1.3" "@xmtp/consent-proof-signature": "npm:^0.1.3" "@xmtp/content-type-primitives": "npm:^1.0.1" @@ -4198,7 +4253,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^6.11.1, ethers@npm:^6.13.1": +"ethers@npm:^6.10.0, ethers@npm:^6.11.1, ethers@npm:^6.13.1": version: 6.13.4 resolution: "ethers@npm:6.13.4" dependencies: @@ -7804,6 +7859,28 @@ __metadata: languageName: node linkType: hard +"viem@npm:^2.16.5": + version: 2.21.29 + resolution: "viem@npm:2.21.29" + dependencies: + "@adraffy/ens-normalize": "npm:1.11.0" + "@noble/curves": "npm:1.6.0" + "@noble/hashes": "npm:1.5.0" + "@scure/bip32": "npm:1.5.0" + "@scure/bip39": "npm:1.4.0" + abitype: "npm:1.0.6" + isows: "npm:1.0.6" + webauthn-p256: "npm:0.0.10" + ws: "npm:8.18.0" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/5f50833efe873860ed5bc67e609494447c9b85aef894905889e0292ef7051490ebd6640f8854e89ed76c218887cb8706cd37feb701046c285374f3fcc6a26867 + languageName: node + linkType: hard + "vite-node@npm:2.1.3": version: 2.1.3 resolution: "vite-node@npm:2.1.3" @@ -8141,7 +8218,7 @@ __metadata: "@eslint/js": "npm:^9.12.0" "@ianvs/prettier-plugin-sort-imports": "npm:^4.3.1" "@types/eslint__js": "npm:^8.42.3" - "@types/node": "npm:^20.16.11" + "@types/node": "npm:^20.16.12" eslint: "npm:^9.12.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-prettier: "npm:^5.2.1" From b5df2c22e6d984e5c1a7eb854c6e76620c6226c5 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 18 Oct 2024 10:54:29 -0500 Subject: [PATCH 48/51] Fix up package.json and configs --- packages/frames-validator/package.json | 13 +++++++------ packages/frames-validator/rollup.config.js | 8 +++++++- packages/frames-validator/vitest.config.ts | 1 - packages/frames-validator/vitest.setup.ts | 5 ----- yarn.lock | 6 +++--- 5 files changed, 17 insertions(+), 16 deletions(-) delete mode 100644 packages/frames-validator/vitest.setup.ts diff --git a/packages/frames-validator/package.json b/packages/frames-validator/package.json index 06e3af906..e0960c0cb 100644 --- a/packages/frames-validator/package.json +++ b/packages/frames-validator/package.json @@ -23,10 +23,11 @@ "dist" ], "scripts": { - "build": "yarn clean && rollup -c", - "clean": "rm -rf dist", - "prepublishOnly": "yarn build", - "test": "vitest run ./src" + "build": "yarn clean:dist && yarn rollup -c", + "clean": "rm -rf .turbo && rm -rf node_modules && yarn clean:dist", + "clean:dist": "rm -rf dist", + "test": "vitest run", + "typecheck": "tsc" }, "dependencies": { "@noble/curves": "^1.3.0", @@ -38,7 +39,7 @@ "@open-frames/types": "^0.1.1", "@rollup/plugin-typescript": "^12.1.1", "@xmtp/frames-client": "^0.5.4", - "@xmtp/xmtp-js": "^11.3.12", + "@xmtp/xmtp-js": "^12.1.0", "ethers": "^6.10.0", "rollup": "^4.24.0", "rollup-plugin-dts": "^6.1.1", @@ -47,7 +48,7 @@ }, "packageManager": "yarn@4.5.0", "engines": { - "node": ">=18" + "node": ">=20" }, "publishConfig": { "access": "public", diff --git a/packages/frames-validator/rollup.config.js b/packages/frames-validator/rollup.config.js index 240d93532..10e2dee96 100644 --- a/packages/frames-validator/rollup.config.js +++ b/packages/frames-validator/rollup.config.js @@ -2,7 +2,13 @@ import typescript from "@rollup/plugin-typescript"; import { defineConfig } from "rollup"; import { dts } from "rollup-plugin-dts"; -const external = ["@xmtp/proto", "node:crypto", "@xmtp/xmtp-js", "long"]; +const external = [ + "@noble/curves/abstract/utils", + "@noble/curves/secp256k1", + "@noble/hashes/sha256", + "@xmtp/proto", + "viem/utils", +]; const plugins = [ typescript({ diff --git a/packages/frames-validator/vitest.config.ts b/packages/frames-validator/vitest.config.ts index 3901d2a6f..d01c48c56 100644 --- a/packages/frames-validator/vitest.config.ts +++ b/packages/frames-validator/vitest.config.ts @@ -10,7 +10,6 @@ const vitestConfig = defineVitestConfig({ test: { globals: true, environment: "node", - setupFiles: "./vitest.setup.ts", }, }); diff --git a/packages/frames-validator/vitest.setup.ts b/packages/frames-validator/vitest.setup.ts deleted file mode 100644 index c0ab99828..000000000 --- a/packages/frames-validator/vitest.setup.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { webcrypto } from "crypto"; - -// eslint-disable-next-line -// @ts-ignore -global.crypto = webcrypto; diff --git a/yarn.lock b/yarn.lock index 4be6d5631..d43a2c227 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2780,7 +2780,7 @@ __metadata: "@rollup/plugin-typescript": "npm:^12.1.1" "@xmtp/frames-client": "npm:^0.5.4" "@xmtp/proto": "npm:3.61.1" - "@xmtp/xmtp-js": "npm:^11.3.12" + "@xmtp/xmtp-js": "npm:^12.1.0" ethers: "npm:^6.10.0" rollup: "npm:^4.24.0" rollup-plugin-dts: "npm:^6.1.1" @@ -2892,7 +2892,7 @@ __metadata: languageName: node linkType: hard -"@xmtp/xmtp-js@npm:^11.3.12, @xmtp/xmtp-js@npm:^11.6.3": +"@xmtp/xmtp-js@npm:^11.6.3": version: 11.6.3 resolution: "@xmtp/xmtp-js@npm:11.6.3" dependencies: @@ -2908,7 +2908,7 @@ __metadata: languageName: node linkType: hard -"@xmtp/xmtp-js@npm:^12.0.0": +"@xmtp/xmtp-js@npm:^12.0.0, @xmtp/xmtp-js@npm:^12.1.0": version: 12.1.0 resolution: "@xmtp/xmtp-js@npm:12.1.0" dependencies: From e6f2fdcc05b5a81da658a35815373430363c41f2 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 18 Oct 2024 10:54:43 -0500 Subject: [PATCH 49/51] Fix lint issues --- packages/frames-validator/src/index.test.ts | 15 ++++++--------- packages/frames-validator/src/openFrames.ts | 18 +++++++++--------- packages/frames-validator/src/types.ts | 2 +- packages/frames-validator/src/utils.ts | 10 +++------- packages/frames-validator/src/validation.ts | 20 ++++++++++---------- 5 files changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/frames-validator/src/index.test.ts b/packages/frames-validator/src/index.test.ts index 6ae6b2123..d61a3f99f 100644 --- a/packages/frames-validator/src/index.test.ts +++ b/packages/frames-validator/src/index.test.ts @@ -29,6 +29,7 @@ describe("validations", () => { client = await Client.create(wallet); framesClient = new FramesClient(client); }); + it("succeeds in the happy path", async () => { const postData = await framesClient.signFrameAction({ buttonIndex: BUTTON_INDEX, @@ -36,7 +37,7 @@ describe("validations", () => { conversationTopic: CONVERSATION_TOPIC, participantAccountAddresses: PARTICIPANT_ACCOUNT_ADDRESSES, }); - const validated = await validateFramesPost(postData); + const validated = validateFramesPost(postData); expect(validated.verifiedWalletAddress).toEqual(client.address); }); @@ -52,12 +53,12 @@ describe("validations", () => { b64Decode(postData.trustedData.messageBytes), ); - if (!deserialized.signature?.ecdsaCompact?.bytes) { + if (!deserialized.signature.ecdsaCompact?.bytes) { throw new Error("Signature bytes are empty"); } deserialized.signature.ecdsaCompact.bytes = scrambleBytes( - deserialized.signature.ecdsaCompact?.bytes, + deserialized.signature.ecdsaCompact.bytes, ); const reserialized = frames.FrameAction.encode({ signature: deserialized.signature, @@ -71,7 +72,7 @@ describe("validations", () => { reserialized.length, ); - expect(validateFramesPost(postData)).rejects.toThrow(); + expect(() => validateFramesPost(postData)).toThrow(); }); it("fails if the wallet address doesn't match", async () => { @@ -86,10 +87,6 @@ describe("validations", () => { b64Decode(postData.trustedData.messageBytes), ); - if (!deserialized.signedPublicKeyBundle) { - throw new Error("Public key bunlde is empty"); - } - const throwAwayWallet = Wallet.createRandom(); const wrongPublicKeyBundle = ( await PrivateKeyBundleV2.generate(throwAwayWallet) @@ -107,6 +104,6 @@ describe("validations", () => { reserialized.length, ); - expect(validateFramesPost(postData)).rejects.toThrow(); + expect(() => validateFramesPost(postData)).toThrow(); }); }); diff --git a/packages/frames-validator/src/openFrames.ts b/packages/frames-validator/src/openFrames.ts index e768c150a..606aab5a0 100644 --- a/packages/frames-validator/src/openFrames.ts +++ b/packages/frames-validator/src/openFrames.ts @@ -1,9 +1,9 @@ -import { +import type { OpenFramesRequest, RequestValidator, ValidationResponse, } from "@open-frames/types"; -import { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types"; +import type { XmtpOpenFramesRequest, XmtpValidationResponse } from "./types"; import { validateFramesPost } from "./validation"; export class XmtpValidator @@ -30,7 +30,7 @@ export class XmtpValidator const isCorrectClientProtocol = protocol === "xmtp"; const isCorrectVersion = version >= this.minProtocolVersionDate; const isTrustedDataValid = - typeof payload.trustedData?.messageBytes === "string"; + typeof payload.trustedData.messageBytes === "string"; return isCorrectClientProtocol && isCorrectVersion && isTrustedDataValid; } @@ -41,16 +41,16 @@ export class XmtpValidator ValidationResponse > { try { - const validationResponse = await validateFramesPost(payload); - return { + const validationResponse = validateFramesPost(payload); + return await Promise.resolve({ isValid: true, clientProtocol: payload.clientProtocol, message: validationResponse, - }; - } catch (error) { - return { + }); + } catch { + return Promise.resolve({ isValid: false, - }; + }); } } } diff --git a/packages/frames-validator/src/types.ts b/packages/frames-validator/src/types.ts index e4b556fc9..8cd435254 100644 --- a/packages/frames-validator/src/types.ts +++ b/packages/frames-validator/src/types.ts @@ -2,7 +2,7 @@ import type { OpenFramesTrustedData, OpenFramesUntrustedData, } from "@open-frames/types"; -import { frames } from "@xmtp/proto"; +import type { frames } from "@xmtp/proto"; export type UntrustedData = OpenFramesUntrustedData & { walletAddress: string; // Untrusted version of the wallet address diff --git a/packages/frames-validator/src/utils.ts b/packages/frames-validator/src/utils.ts index df1cc422a..fb18ae07d 100644 --- a/packages/frames-validator/src/utils.ts +++ b/packages/frames-validator/src/utils.ts @@ -1,8 +1,8 @@ import { bytesToHex } from "@noble/curves/abstract/utils"; import { secp256k1 } from "@noble/curves/secp256k1"; import { sha256 } from "@noble/hashes/sha256"; -import { publicKey, signature } from "@xmtp/proto"; -import { SignedPublicKeyBundle } from "@xmtp/xmtp-js"; +import { publicKey, type signature } from "@xmtp/proto"; +import type { SignedPublicKeyBundle } from "@xmtp/xmtp-js"; import { getAddress, hashMessage, @@ -113,7 +113,7 @@ export function verifyIdentityKeySignature( } function computeAddress(bytes: Uint8Array) { - const publicKey = viemBytesToHex(bytes.slice(1)) as `0x${string}`; + const publicKey = viemBytesToHex(bytes.slice(1)); const hash = keccak256(publicKey); const address = hash.substring(hash.length - 40); return getAddress(`0x${address}`); @@ -151,9 +151,5 @@ export function verifyWalletSignature( const walletAddress = recoverWalletAddress(signatureText, toVerify); - if (!walletAddress) { - throw new Error("Could not recover wallet address"); - } - return walletAddress; } diff --git a/packages/frames-validator/src/validation.ts b/packages/frames-validator/src/validation.ts index a26a6b6b1..895e0dd86 100644 --- a/packages/frames-validator/src/validation.ts +++ b/packages/frames-validator/src/validation.ts @@ -1,18 +1,18 @@ -import { fetcher, frames, publicKey, signature } from "@xmtp/proto"; -import { +import { fetcher, frames, type publicKey, type signature } from "@xmtp/proto"; +import type { UntrustedData, XmtpOpenFramesRequest, XmtpValidationResponse, } from "./types.js"; import { verifyIdentityKeySignature, verifyWalletSignature } from "./utils.js"; -export * from "./types.js"; +export type * from "./types.js"; const { b64Decode } = fetcher; -export async function validateFramesPost( +export function validateFramesPost( data: XmtpOpenFramesRequest, -): Promise { +): XmtpValidationResponse { const { untrustedData, trustedData } = data; const { walletAddress } = untrustedData; const { messageBytes: messageBytesString } = trustedData; @@ -22,7 +22,7 @@ export async function validateFramesPost( const { actionBody, actionBodyBytes, signature, signedPublicKeyBundle } = deserializeProtoMessage(messageBytes); - const verifiedWalletAddress = await getVerifiedWalletAddress( + const verifiedWalletAddress = getVerifiedWalletAddress( actionBodyBytes, signature, signedPublicKeyBundle, @@ -33,7 +33,7 @@ export async function validateFramesPost( throw new Error("Invalid wallet address"); } - await checkUntrustedData(untrustedData, actionBody); + checkUntrustedData(untrustedData, actionBody); return { actionBody, @@ -58,18 +58,18 @@ export function deserializeProtoMessage(messageBytes: Uint8Array) { }; } -async function getVerifiedWalletAddress( +function getVerifiedWalletAddress( actionBodyBytes: Uint8Array, signature: signature.Signature, signedPublicKeyBundle: publicKey.SignedPublicKeyBundle, -): Promise { +): string { const walletAddress = verifyWalletSignature(signedPublicKeyBundle); verifyIdentityKeySignature(actionBodyBytes, signature, signedPublicKeyBundle); return walletAddress; } -async function checkUntrustedData( +function checkUntrustedData( { url, buttonIndex, From 4008b4c277fff8accc67f8a4bdd8474a1dfda37b Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 18 Oct 2024 11:26:49 -0500 Subject: [PATCH 50/51] Update yarn.lock --- yarn.lock | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/yarn.lock b/yarn.lock index d43a2c227..26f3a0909 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2719,29 +2719,6 @@ __metadata: languageName: unknown linkType: soft -"@xmtp/encryption@workspace:shared/encryption": - version: 0.0.0-use.local - resolution: "@xmtp/encryption@workspace:shared/encryption" - dependencies: - "@rollup/plugin-terser": "npm:^0.4.4" - "@rollup/plugin-typescript": "npm:^12.1.1" - "@types/node": "npm:^20.16.12" - "@vitest/coverage-v8": "npm:^2.1.3" - "@xmtp/proto": "npm:^3.68.0" - "@xmtp/rollup-plugin-resolve-extensions": "npm:1.0.1" - happy-dom: "npm:^15.7.4" - rimraf: "npm:^6.0.1" - rollup: "npm:^4.24.0" - rollup-plugin-dts: "npm:^6.1.1" - rollup-plugin-filesize: "npm:^10.0.0" - rollup-plugin-tsconfig-paths: "npm:^1.5.2" - typescript: "npm:^5.6.3" - vite: "npm:^5.4.9" - vite-tsconfig-paths: "npm:^5.0.1" - vitest: "npm:^2.1.3" - languageName: unknown - linkType: soft - "@xmtp/frames-client@npm:^0.5.4, @xmtp/frames-client@workspace:packages/frames-client": version: 0.0.0-use.local resolution: "@xmtp/frames-client@workspace:packages/frames-client" From f6c756bc72b90fbb01af507d88a267ce644cc867 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 18 Oct 2024 11:35:07 -0500 Subject: [PATCH 51/51] Fix turbo config --- turbo.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/turbo.json b/turbo.json index 52268248f..e50811ac8 100644 --- a/turbo.json +++ b/turbo.json @@ -41,6 +41,13 @@ "@xmtp/content-type-text#build" ], "outputs": [] + }, + "@xmtp/frames-validator#test": { + "dependsOn": [ + "@xmtp/content-type-text#build", + "@xmtp/frames-client#build" + ], + "outputs": [] } } }