From 7376b814756e5bab6d48e9847b71e92503ca4cd2 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Fri, 1 Mar 2024 15:54:36 +1100 Subject: [PATCH] feat: tighten verify --- package.json | 3 +- pnpm-lock.yaml | 123 ++ pnpm-workspace.yaml | 1 + protobufs/buf.gen.yaml | 6 + protobufs/package.json | 13 + protobufs/schemas/message.proto | 184 +++ protobufs/schemas/username_proof.proto | 16 + src/protobufs/generated/message_pb.ts | 1176 ++++++++++++++++++ src/protobufs/generated/username_proof_pb.ts | 101 ++ src/utils/requestToContext.ts | 18 +- src/utils/verifyFrame.ts | 41 +- 11 files changed, 1668 insertions(+), 14 deletions(-) create mode 100644 protobufs/buf.gen.yaml create mode 100644 protobufs/package.json create mode 100644 protobufs/schemas/message.proto create mode 100644 protobufs/schemas/username_proof.proto create mode 100644 src/protobufs/generated/message_pb.ts create mode 100644 src/protobufs/generated/username_proof_pb.ts diff --git a/package.json b/package.json index 206f7d99..9656dc67 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "scripts": { "dev": "pnpm --filter playground dev", "dev:create-frog": "bun create-frog/bin.ts", - "build": "pnpm clean && pnpm build:frog && pnpm build:create-frog && bun .scripts/postbuild.ts", + "build": "pnpm clean && pnpm protobufs:generate && pnpm build:frog && pnpm build:create-frog && bun .scripts/postbuild.ts", "build:frog": "tsc --project ./tsconfig.build.json", "build:create-frog": "rimraf create-frog/_lib && tsc -p create-frog/tsconfig.build.json", "changeset": "changeset", @@ -16,6 +16,7 @@ "lint": "biome check . --apply-unsafe", "postinstall": "pnpm build && pnpm preconstruct", "preconstruct": "bun .scripts/preconstruct.ts", + "protobufs:generate": "pnpm --filter protobufs generate", "test": "vitest", "typecheck": "tsc --noEmit" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10e4d967..e4e6b4c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,6 +97,21 @@ importers: specifier: ^4.7.1 version: 4.7.1 + protobufs: + dependencies: + '@bufbuild/buf': + specifier: ^1.29.0 + version: 1.29.0 + '@bufbuild/protobuf': + specifier: ^1.7.2 + version: 1.7.2 + '@bufbuild/protoc-gen-es': + specifier: ^1.7.2 + version: 1.7.2(@bufbuild/protobuf@1.7.2) + buf: + specifier: ^0.1.1 + version: 0.1.1 + services/auth: dependencies: hono: @@ -648,6 +663,104 @@ packages: dev: true optional: true + /@bufbuild/buf-darwin-arm64@1.29.0: + resolution: {integrity: sha512-5hKxsARoY2WpWq1n5ONFqqGuauHb4yILKXCy37KRYCKiRLWmIP5yI3gWvWHKoH7sUJWTQmBqdJoCvYQr6ahQnw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf-darwin-x64@1.29.0: + resolution: {integrity: sha512-wOAPxbPLBns4AHiComWtdO1sx1J1p6mDYTbqmloHuI+B5U2rDbMsoHoe4nBcoMF8+RHxoqjypha29wVo6yzbZg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf-linux-aarch64@1.29.0: + resolution: {integrity: sha512-jLk2J/wyyM7KNJ/DkLfhy3eS2/Bdb70e/56adMkapSoLJmghnpgxW+oFznMxxQUX5I9BU5hTn1UhDFxgLwhP7g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf-linux-x64@1.29.0: + resolution: {integrity: sha512-heLOywj3Oaoh69RnTx7tHsuz6rEnvz77bghLEOghsrjBR6Jcpcwc137EZR4kRTIWJNrE8Kmo3RVeXlv144qQIQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf-win32-arm64@1.29.0: + resolution: {integrity: sha512-Eglyvr3PLqVucuHBcQ61conyBgH9BRaoLpKWcce1gYBVlxMQM1NxjVjGOWihxQ1dXXw5qZXmYfVODf3gSwPMuQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf-win32-x64@1.29.0: + resolution: {integrity: sha512-wRk6co+nqHqEq4iLolXgej0jUVlWlTtGHjKaq54lTbKZrwxrBgql6qS06abgNPRASX0++XT9m3QRZ97qEIC/HQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@bufbuild/buf@1.29.0: + resolution: {integrity: sha512-euksXeFtvlvAV5j94LqXb69qQcJvFfo8vN1d3cx+IzhOKoipykuQQTq7mOWVo2R0kdk6yIMBLBofOYOsh0Df8g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@bufbuild/buf-darwin-arm64': 1.29.0 + '@bufbuild/buf-darwin-x64': 1.29.0 + '@bufbuild/buf-linux-aarch64': 1.29.0 + '@bufbuild/buf-linux-x64': 1.29.0 + '@bufbuild/buf-win32-arm64': 1.29.0 + '@bufbuild/buf-win32-x64': 1.29.0 + dev: false + + /@bufbuild/protobuf@1.7.2: + resolution: {integrity: sha512-i5GE2Dk5ekdlK1TR7SugY4LWRrKSfb5T1Qn4unpIMbfxoeGKERKQ59HG3iYewacGD10SR7UzevfPnh6my4tNmQ==} + dev: false + + /@bufbuild/protoc-gen-es@1.7.2(@bufbuild/protobuf@1.7.2): + resolution: {integrity: sha512-yiRk/T+YGmpSVvIkybCjPt+QyM/pLWMO+MAiz6auvCsiAgfXfc5nFFosD4yBYXID55M6eIkgBcity1AoJ6I30A==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + '@bufbuild/protobuf': 1.7.2 + peerDependenciesMeta: + '@bufbuild/protobuf': + optional: true + dependencies: + '@bufbuild/protobuf': 1.7.2 + '@bufbuild/protoplugin': 1.7.2 + transitivePeerDependencies: + - supports-color + dev: false + + /@bufbuild/protoplugin@1.7.2: + resolution: {integrity: sha512-N3QtO8XWD4F4alMtASWtxBw6BWXp4aLz7rPBXH4KTULdjpUHnq46g15TsrG0/8szZw6pIklTO3lFe14dl6ZYdA==} + dependencies: + '@bufbuild/protobuf': 1.7.2 + '@typescript/vfs': 1.5.0 + typescript: 4.5.2 + transitivePeerDependencies: + - supports-color + dev: false + /@changesets/apply-release-plan@7.0.0: resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==} dependencies: @@ -3997,6 +4110,10 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.23.0) dev: false + /buf@0.1.1: + resolution: {integrity: sha512-mhZY7GswAAd9ZJpBCsf2WaH2WMZwvxgsXD6rozflWgerE6gz2bCT/FvAfzcBXw55R18Jf8Fjzte7bw5xhwVhFg==} + dev: false + /buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true @@ -9367,6 +9484,12 @@ packages: typed-html: 3.0.1 dev: true + /typescript@4.5.2: + resolution: {integrity: sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f14b95cf..f63365b6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - 'create-frog' - 'playground' + - 'protobufs' - 'services/*' - 'site' - 'src' diff --git a/protobufs/buf.gen.yaml b/protobufs/buf.gen.yaml new file mode 100644 index 00000000..001c5fac --- /dev/null +++ b/protobufs/buf.gen.yaml @@ -0,0 +1,6 @@ +# Learn more: https://docs.buf.build/configuration/v1/buf-gen-yaml +version: v1 +plugins: + - plugin: es + opt: target=ts + out: ../src/protobufs/generated \ No newline at end of file diff --git a/protobufs/package.json b/protobufs/package.json new file mode 100644 index 00000000..1849f487 --- /dev/null +++ b/protobufs/package.json @@ -0,0 +1,13 @@ +{ + "name": "protobufs", + "private": true, + "scripts": { + "generate": "buf generate schemas" + }, + "dependencies": { + "@bufbuild/buf": "^1.29.0", + "@bufbuild/protobuf": "^1.7.2", + "@bufbuild/protoc-gen-es": "^1.7.2", + "buf": "^0.1.1" + } +} diff --git a/protobufs/schemas/message.proto b/protobufs/schemas/message.proto new file mode 100644 index 00000000..3f5d7f26 --- /dev/null +++ b/protobufs/schemas/message.proto @@ -0,0 +1,184 @@ +syntax = "proto3"; +import "username_proof.proto"; + +/** + * A Message is a delta operation on the Farcaster network. The message protobuf is an envelope + * that wraps a MessageData object and contains a hash and signature which can verify its authenticity. + */ +message Message { + MessageData data = 1; // Contents of the message + bytes hash = 2; // Hash digest of data + HashScheme hash_scheme = 3; // Hash scheme that produced the hash digest + bytes signature = 4; // Signature of the hash digest + SignatureScheme signature_scheme = 5; // Signature scheme that produced the signature + bytes signer = 6; // Public key or address of the key pair that produced the signature + optional bytes data_bytes = 7; // MessageData serialized to bytes if using protobuf serialization other than ts-proto +} + + /** + * A MessageData object contains properties common to all messages and wraps a body object which + * contains properties specific to the MessageType. + */ +message MessageData { + MessageType type = 1; // Type of message contained in the body + uint64 fid = 2; // Farcaster ID of the user producing the message + uint32 timestamp = 3; // Farcaster epoch timestamp in seconds + FarcasterNetwork network = 4; // Farcaster network the message is intended for + oneof body { + CastAddBody cast_add_body = 5; + CastRemoveBody cast_remove_body = 6; + ReactionBody reaction_body = 7; + VerificationAddAddressBody verification_add_address_body = 9; + VerificationRemoveBody verification_remove_body = 10; + // SignerAddBody signer_add_body = 11; // Deprecated + UserDataBody user_data_body = 12; + // SignerRemoveBody signer_remove_body = 13; // Deprecated + LinkBody link_body = 14; + UserNameProof username_proof_body = 15; + FrameActionBody frame_action_body = 16; + } // Properties specific to the MessageType +} + +/** Type of hashing scheme used to produce a digest of MessageData */ +enum HashScheme { + HASH_SCHEME_NONE = 0; + HASH_SCHEME_BLAKE3 = 1; // Default scheme for hashing MessageData +} + +/** Type of signature scheme used to sign the Message hash */ +enum SignatureScheme { + SIGNATURE_SCHEME_NONE = 0; + SIGNATURE_SCHEME_ED25519 = 1; // Ed25519 signature (default) + SIGNATURE_SCHEME_EIP712 = 2; // ECDSA signature using EIP-712 scheme +} + +/** Type of the MessageBody */ +enum MessageType { + MESSAGE_TYPE_NONE = 0; + MESSAGE_TYPE_CAST_ADD = 1; // Add a new Cast + MESSAGE_TYPE_CAST_REMOVE = 2; // Remove an existing Cast + MESSAGE_TYPE_REACTION_ADD = 3; // Add a Reaction to a Cast + MESSAGE_TYPE_REACTION_REMOVE = 4; // Remove a Reaction from a Cast + MESSAGE_TYPE_LINK_ADD = 5; // Add a new Link + MESSAGE_TYPE_LINK_REMOVE = 6; // Remove an existing Link + MESSAGE_TYPE_VERIFICATION_ADD_ETH_ADDRESS = 7; // Add a Verification of an Ethereum Address + MESSAGE_TYPE_VERIFICATION_REMOVE = 8; // Remove a Verification +// Deprecated +// MESSAGE_TYPE_SIGNER_ADD = 9; // Add a new Ed25519 key pair that signs messages for a user +// MESSAGE_TYPE_SIGNER_REMOVE = 10; // Remove an Ed25519 key pair that signs messages for a user + MESSAGE_TYPE_USER_DATA_ADD = 11; // Add metadata about a user + MESSAGE_TYPE_USERNAME_PROOF = 12; // Add or replace a username proof + MESSAGE_TYPE_FRAME_ACTION = 13; // A Farcaster Frame action +} + +/** Farcaster network the message is intended for */ +enum FarcasterNetwork { + FARCASTER_NETWORK_NONE = 0; + FARCASTER_NETWORK_MAINNET = 1; // Public primary network + FARCASTER_NETWORK_TESTNET = 2; // Public test network + FARCASTER_NETWORK_DEVNET = 3; // Private test network +} + +/** Adds metadata about a user */ +message UserDataBody { + UserDataType type = 1; // Type of metadata + string value = 2; // Value of the metadata +} + +/** Type of UserData */ +enum UserDataType { + USER_DATA_TYPE_NONE = 0; + USER_DATA_TYPE_PFP = 1; // Profile Picture for the user + USER_DATA_TYPE_DISPLAY = 2; // Display Name for the user + USER_DATA_TYPE_BIO = 3; // Bio for the user + USER_DATA_TYPE_URL = 5; // URL of the user + USER_DATA_TYPE_USERNAME = 6; // Preferred Name for the user +} + +message Embed { + oneof embed { + string url = 1; + CastId cast_id = 2; + } +} + +/** Adds a new Cast */ +message CastAddBody { + repeated string embeds_deprecated = 1; // URLs to be embedded in the cast + repeated uint64 mentions = 2; // Fids mentioned in the cast + oneof parent { + CastId parent_cast_id = 3; // Parent cast of the cast + string parent_url = 7; // Parent URL + }; + string text = 4; // Text of the cast + repeated uint32 mentions_positions = 5; // Positions of the mentions in the text + repeated Embed embeds = 6; // URLs or cast ids to be embedded in the cast +} + +/** Removes an existing Cast */ +message CastRemoveBody { + bytes target_hash = 1; // Hash of the cast to remove +} + +/** Identifier used to look up a Cast */ +message CastId { + uint64 fid = 1; // Fid of the user who created the cast + bytes hash = 2; // Hash of the cast +} + +/** Adds or removes a Reaction from a Cast */ +message ReactionBody { + ReactionType type = 1; // Type of reaction + oneof target { + CastId target_cast_id = 2; // CastId of the Cast to react to + string target_url = 3; // URL to react to + } +} + +/** Type of Reaction */ +enum ReactionType { + REACTION_TYPE_NONE = 0; + REACTION_TYPE_LIKE = 1; // Like the target cast + REACTION_TYPE_RECAST = 2; // Share target cast to the user's audience +} + +/** Type of Protocol to disambiguate verification addresses */ +enum Protocol { + PROTOCOL_ETHEREUM = 0; + PROTOCOL_SOLANA = 1; +} + +/** Adds a Verification of ownership of an Address based on Protocol */ +message VerificationAddAddressBody { + bytes address = 1; // Address being verified for a given Protocol + bytes claim_signature = 2; // Signature produced by the user's address for a given Protocol + bytes block_hash = 3; // Hash of the latest Ethereum block when the signature was produced + uint32 verification_type = 4; // Type of verification. 0 = EOA, 1 = contract + uint32 chain_id = 5; // 0 for EOA verifications, 1 or 10 for contract verifications + Protocol protocol = 7; // Protocol of the Verification +} + +/** Removes a Verification of a given protocol */ +message VerificationRemoveBody { + bytes address = 1; // Address of the Verification to remove + Protocol protocol = 2; // Protocol of the Verification to remove +} + +/** Adds or removes a Link */ +message LinkBody { + string type = 1; // Type of link, <= 8 characters + optional uint32 displayTimestamp = 2; // User-defined timestamp that preserves original timestamp when message.data.timestamp needs to be updated for compaction + oneof target { + uint64 target_fid = 3; // The fid the link relates to + } +} + +/** A Farcaster Frame action */ +message FrameActionBody { + bytes url = 1; // URL of the Frame triggering the action + uint32 button_index = 2; // The index of the button pressed (1-4) + CastId cast_id = 3; // The cast which contained the frame url + bytes input_text = 4; // Text input from the user, if present + bytes state = 5; // Serialized frame state value + bytes transaction_id = 6; // Chain-specific transaction ID for tx actions +} diff --git a/protobufs/schemas/username_proof.proto b/protobufs/schemas/username_proof.proto new file mode 100644 index 00000000..3a888f0e --- /dev/null +++ b/protobufs/schemas/username_proof.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +enum UserNameType { + USERNAME_TYPE_NONE = 0; + USERNAME_TYPE_FNAME = 1; + USERNAME_TYPE_ENS_L1 = 2; +} + +message UserNameProof { + uint64 timestamp = 1; + bytes name = 2; + bytes owner = 3; + bytes signature = 4; + uint64 fid = 5; + UserNameType type = 6; +} \ No newline at end of file diff --git a/src/protobufs/generated/message_pb.ts b/src/protobufs/generated/message_pb.ts new file mode 100644 index 00000000..77caca4e --- /dev/null +++ b/src/protobufs/generated/message_pb.ts @@ -0,0 +1,1176 @@ +// @generated by protoc-gen-es v1.7.2 with parameter "target=ts" +// @generated from file message.proto (syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message as Message$1, proto3, protoInt64 } from "@bufbuild/protobuf"; +import { UserNameProof } from "./username_proof_pb.js"; + +/** + * * Type of hashing scheme used to produce a digest of MessageData + * + * @generated from enum HashScheme + */ +export enum HashScheme { + /** + * @generated from enum value: HASH_SCHEME_NONE = 0; + */ + NONE = 0, + + /** + * Default scheme for hashing MessageData + * + * @generated from enum value: HASH_SCHEME_BLAKE3 = 1; + */ + BLAKE3 = 1, +} +// Retrieve enum metadata with: proto3.getEnumType(HashScheme) +proto3.util.setEnumType(HashScheme, "HashScheme", [ + { no: 0, name: "HASH_SCHEME_NONE" }, + { no: 1, name: "HASH_SCHEME_BLAKE3" }, +]); + +/** + * * Type of signature scheme used to sign the Message hash + * + * @generated from enum SignatureScheme + */ +export enum SignatureScheme { + /** + * @generated from enum value: SIGNATURE_SCHEME_NONE = 0; + */ + NONE = 0, + + /** + * Ed25519 signature (default) + * + * @generated from enum value: SIGNATURE_SCHEME_ED25519 = 1; + */ + ED25519 = 1, + + /** + * ECDSA signature using EIP-712 scheme + * + * @generated from enum value: SIGNATURE_SCHEME_EIP712 = 2; + */ + EIP712 = 2, +} +// Retrieve enum metadata with: proto3.getEnumType(SignatureScheme) +proto3.util.setEnumType(SignatureScheme, "SignatureScheme", [ + { no: 0, name: "SIGNATURE_SCHEME_NONE" }, + { no: 1, name: "SIGNATURE_SCHEME_ED25519" }, + { no: 2, name: "SIGNATURE_SCHEME_EIP712" }, +]); + +/** + * * Type of the MessageBody + * + * @generated from enum MessageType + */ +export enum MessageType { + /** + * @generated from enum value: MESSAGE_TYPE_NONE = 0; + */ + NONE = 0, + + /** + * Add a new Cast + * + * @generated from enum value: MESSAGE_TYPE_CAST_ADD = 1; + */ + CAST_ADD = 1, + + /** + * Remove an existing Cast + * + * @generated from enum value: MESSAGE_TYPE_CAST_REMOVE = 2; + */ + CAST_REMOVE = 2, + + /** + * Add a Reaction to a Cast + * + * @generated from enum value: MESSAGE_TYPE_REACTION_ADD = 3; + */ + REACTION_ADD = 3, + + /** + * Remove a Reaction from a Cast + * + * @generated from enum value: MESSAGE_TYPE_REACTION_REMOVE = 4; + */ + REACTION_REMOVE = 4, + + /** + * Add a new Link + * + * @generated from enum value: MESSAGE_TYPE_LINK_ADD = 5; + */ + LINK_ADD = 5, + + /** + * Remove an existing Link + * + * @generated from enum value: MESSAGE_TYPE_LINK_REMOVE = 6; + */ + LINK_REMOVE = 6, + + /** + * Add a Verification of an Ethereum Address + * + * @generated from enum value: MESSAGE_TYPE_VERIFICATION_ADD_ETH_ADDRESS = 7; + */ + VERIFICATION_ADD_ETH_ADDRESS = 7, + + /** + * Remove a Verification + * + * @generated from enum value: MESSAGE_TYPE_VERIFICATION_REMOVE = 8; + */ + VERIFICATION_REMOVE = 8, + + /** + * Deprecated + * MESSAGE_TYPE_SIGNER_ADD = 9; // Add a new Ed25519 key pair that signs messages for a user + * MESSAGE_TYPE_SIGNER_REMOVE = 10; // Remove an Ed25519 key pair that signs messages for a user + * + * Add metadata about a user + * + * @generated from enum value: MESSAGE_TYPE_USER_DATA_ADD = 11; + */ + USER_DATA_ADD = 11, + + /** + * Add or replace a username proof + * + * @generated from enum value: MESSAGE_TYPE_USERNAME_PROOF = 12; + */ + USERNAME_PROOF = 12, + + /** + * A Farcaster Frame action + * + * @generated from enum value: MESSAGE_TYPE_FRAME_ACTION = 13; + */ + FRAME_ACTION = 13, +} +// Retrieve enum metadata with: proto3.getEnumType(MessageType) +proto3.util.setEnumType(MessageType, "MessageType", [ + { no: 0, name: "MESSAGE_TYPE_NONE" }, + { no: 1, name: "MESSAGE_TYPE_CAST_ADD" }, + { no: 2, name: "MESSAGE_TYPE_CAST_REMOVE" }, + { no: 3, name: "MESSAGE_TYPE_REACTION_ADD" }, + { no: 4, name: "MESSAGE_TYPE_REACTION_REMOVE" }, + { no: 5, name: "MESSAGE_TYPE_LINK_ADD" }, + { no: 6, name: "MESSAGE_TYPE_LINK_REMOVE" }, + { no: 7, name: "MESSAGE_TYPE_VERIFICATION_ADD_ETH_ADDRESS" }, + { no: 8, name: "MESSAGE_TYPE_VERIFICATION_REMOVE" }, + { no: 11, name: "MESSAGE_TYPE_USER_DATA_ADD" }, + { no: 12, name: "MESSAGE_TYPE_USERNAME_PROOF" }, + { no: 13, name: "MESSAGE_TYPE_FRAME_ACTION" }, +]); + +/** + * * Farcaster network the message is intended for + * + * @generated from enum FarcasterNetwork + */ +export enum FarcasterNetwork { + /** + * @generated from enum value: FARCASTER_NETWORK_NONE = 0; + */ + NONE = 0, + + /** + * Public primary network + * + * @generated from enum value: FARCASTER_NETWORK_MAINNET = 1; + */ + MAINNET = 1, + + /** + * Public test network + * + * @generated from enum value: FARCASTER_NETWORK_TESTNET = 2; + */ + TESTNET = 2, + + /** + * Private test network + * + * @generated from enum value: FARCASTER_NETWORK_DEVNET = 3; + */ + DEVNET = 3, +} +// Retrieve enum metadata with: proto3.getEnumType(FarcasterNetwork) +proto3.util.setEnumType(FarcasterNetwork, "FarcasterNetwork", [ + { no: 0, name: "FARCASTER_NETWORK_NONE" }, + { no: 1, name: "FARCASTER_NETWORK_MAINNET" }, + { no: 2, name: "FARCASTER_NETWORK_TESTNET" }, + { no: 3, name: "FARCASTER_NETWORK_DEVNET" }, +]); + +/** + * * Type of UserData + * + * @generated from enum UserDataType + */ +export enum UserDataType { + /** + * @generated from enum value: USER_DATA_TYPE_NONE = 0; + */ + NONE = 0, + + /** + * Profile Picture for the user + * + * @generated from enum value: USER_DATA_TYPE_PFP = 1; + */ + PFP = 1, + + /** + * Display Name for the user + * + * @generated from enum value: USER_DATA_TYPE_DISPLAY = 2; + */ + DISPLAY = 2, + + /** + * Bio for the user + * + * @generated from enum value: USER_DATA_TYPE_BIO = 3; + */ + BIO = 3, + + /** + * URL of the user + * + * @generated from enum value: USER_DATA_TYPE_URL = 5; + */ + URL = 5, + + /** + * Preferred Name for the user + * + * @generated from enum value: USER_DATA_TYPE_USERNAME = 6; + */ + USERNAME = 6, +} +// Retrieve enum metadata with: proto3.getEnumType(UserDataType) +proto3.util.setEnumType(UserDataType, "UserDataType", [ + { no: 0, name: "USER_DATA_TYPE_NONE" }, + { no: 1, name: "USER_DATA_TYPE_PFP" }, + { no: 2, name: "USER_DATA_TYPE_DISPLAY" }, + { no: 3, name: "USER_DATA_TYPE_BIO" }, + { no: 5, name: "USER_DATA_TYPE_URL" }, + { no: 6, name: "USER_DATA_TYPE_USERNAME" }, +]); + +/** + * * Type of Reaction + * + * @generated from enum ReactionType + */ +export enum ReactionType { + /** + * @generated from enum value: REACTION_TYPE_NONE = 0; + */ + NONE = 0, + + /** + * Like the target cast + * + * @generated from enum value: REACTION_TYPE_LIKE = 1; + */ + LIKE = 1, + + /** + * Share target cast to the user's audience + * + * @generated from enum value: REACTION_TYPE_RECAST = 2; + */ + RECAST = 2, +} +// Retrieve enum metadata with: proto3.getEnumType(ReactionType) +proto3.util.setEnumType(ReactionType, "ReactionType", [ + { no: 0, name: "REACTION_TYPE_NONE" }, + { no: 1, name: "REACTION_TYPE_LIKE" }, + { no: 2, name: "REACTION_TYPE_RECAST" }, +]); + +/** + * * Type of Protocol to disambiguate verification addresses + * + * @generated from enum Protocol + */ +export enum Protocol { + /** + * @generated from enum value: PROTOCOL_ETHEREUM = 0; + */ + ETHEREUM = 0, + + /** + * @generated from enum value: PROTOCOL_SOLANA = 1; + */ + SOLANA = 1, +} +// Retrieve enum metadata with: proto3.getEnumType(Protocol) +proto3.util.setEnumType(Protocol, "Protocol", [ + { no: 0, name: "PROTOCOL_ETHEREUM" }, + { no: 1, name: "PROTOCOL_SOLANA" }, +]); + +/** + * * + * A Message is a delta operation on the Farcaster network. The message protobuf is an envelope + * that wraps a MessageData object and contains a hash and signature which can verify its authenticity. + * + * @generated from message Message + */ +export class Message extends Message$1 { + /** + * Contents of the message + * + * @generated from field: MessageData data = 1; + */ + data?: MessageData; + + /** + * Hash digest of data + * + * @generated from field: bytes hash = 2; + */ + hash = new Uint8Array(0); + + /** + * Hash scheme that produced the hash digest + * + * @generated from field: HashScheme hash_scheme = 3; + */ + hashScheme = HashScheme.NONE; + + /** + * Signature of the hash digest + * + * @generated from field: bytes signature = 4; + */ + signature = new Uint8Array(0); + + /** + * Signature scheme that produced the signature + * + * @generated from field: SignatureScheme signature_scheme = 5; + */ + signatureScheme = SignatureScheme.NONE; + + /** + * Public key or address of the key pair that produced the signature + * + * @generated from field: bytes signer = 6; + */ + signer = new Uint8Array(0); + + /** + * MessageData serialized to bytes if using protobuf serialization other than ts-proto + * + * @generated from field: optional bytes data_bytes = 7; + */ + dataBytes?: Uint8Array; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "Message"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "data", kind: "message", T: MessageData }, + { no: 2, name: "hash", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 3, name: "hash_scheme", kind: "enum", T: proto3.getEnumType(HashScheme) }, + { no: 4, name: "signature", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 5, name: "signature_scheme", kind: "enum", T: proto3.getEnumType(SignatureScheme) }, + { no: 6, name: "signer", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 7, name: "data_bytes", kind: "scalar", T: 12 /* ScalarType.BYTES */, opt: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Message { + return new Message().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Message { + return new Message().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Message { + return new Message().fromJsonString(jsonString, options); + } + + static equals(a: Message | PlainMessage | undefined, b: Message | PlainMessage | undefined): boolean { + return proto3.util.equals(Message, a, b); + } +} + +/** + * * + * A MessageData object contains properties common to all messages and wraps a body object which + * contains properties specific to the MessageType. + * + * @generated from message MessageData + */ +export class MessageData extends Message$1 { + /** + * Type of message contained in the body + * + * @generated from field: MessageType type = 1; + */ + type = MessageType.NONE; + + /** + * Farcaster ID of the user producing the message + * + * @generated from field: uint64 fid = 2; + */ + fid = protoInt64.zero; + + /** + * Farcaster epoch timestamp in seconds + * + * @generated from field: uint32 timestamp = 3; + */ + timestamp = 0; + + /** + * Farcaster network the message is intended for + * + * @generated from field: FarcasterNetwork network = 4; + */ + network = FarcasterNetwork.NONE; + + /** + * @generated from oneof MessageData.body + */ + body: { + /** + * @generated from field: CastAddBody cast_add_body = 5; + */ + value: CastAddBody; + case: "castAddBody"; + } | { + /** + * @generated from field: CastRemoveBody cast_remove_body = 6; + */ + value: CastRemoveBody; + case: "castRemoveBody"; + } | { + /** + * @generated from field: ReactionBody reaction_body = 7; + */ + value: ReactionBody; + case: "reactionBody"; + } | { + /** + * @generated from field: VerificationAddAddressBody verification_add_address_body = 9; + */ + value: VerificationAddAddressBody; + case: "verificationAddAddressBody"; + } | { + /** + * @generated from field: VerificationRemoveBody verification_remove_body = 10; + */ + value: VerificationRemoveBody; + case: "verificationRemoveBody"; + } | { + /** + * SignerAddBody signer_add_body = 11; // Deprecated + * + * @generated from field: UserDataBody user_data_body = 12; + */ + value: UserDataBody; + case: "userDataBody"; + } | { + /** + * SignerRemoveBody signer_remove_body = 13; // Deprecated + * + * @generated from field: LinkBody link_body = 14; + */ + value: LinkBody; + case: "linkBody"; + } | { + /** + * @generated from field: UserNameProof username_proof_body = 15; + */ + value: UserNameProof; + case: "usernameProofBody"; + } | { + /** + * @generated from field: FrameActionBody frame_action_body = 16; + */ + value: FrameActionBody; + case: "frameActionBody"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "MessageData"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "type", kind: "enum", T: proto3.getEnumType(MessageType) }, + { no: 2, name: "fid", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, + { no: 3, name: "timestamp", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, + { no: 4, name: "network", kind: "enum", T: proto3.getEnumType(FarcasterNetwork) }, + { no: 5, name: "cast_add_body", kind: "message", T: CastAddBody, oneof: "body" }, + { no: 6, name: "cast_remove_body", kind: "message", T: CastRemoveBody, oneof: "body" }, + { no: 7, name: "reaction_body", kind: "message", T: ReactionBody, oneof: "body" }, + { no: 9, name: "verification_add_address_body", kind: "message", T: VerificationAddAddressBody, oneof: "body" }, + { no: 10, name: "verification_remove_body", kind: "message", T: VerificationRemoveBody, oneof: "body" }, + { no: 12, name: "user_data_body", kind: "message", T: UserDataBody, oneof: "body" }, + { no: 14, name: "link_body", kind: "message", T: LinkBody, oneof: "body" }, + { no: 15, name: "username_proof_body", kind: "message", T: UserNameProof, oneof: "body" }, + { no: 16, name: "frame_action_body", kind: "message", T: FrameActionBody, oneof: "body" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): MessageData { + return new MessageData().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): MessageData { + return new MessageData().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): MessageData { + return new MessageData().fromJsonString(jsonString, options); + } + + static equals(a: MessageData | PlainMessage | undefined, b: MessageData | PlainMessage | undefined): boolean { + return proto3.util.equals(MessageData, a, b); + } +} + +/** + * * Adds metadata about a user + * + * @generated from message UserDataBody + */ +export class UserDataBody extends Message$1 { + /** + * Type of metadata + * + * @generated from field: UserDataType type = 1; + */ + type = UserDataType.NONE; + + /** + * Value of the metadata + * + * @generated from field: string value = 2; + */ + value = ""; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "UserDataBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "type", kind: "enum", T: proto3.getEnumType(UserDataType) }, + { no: 2, name: "value", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): UserDataBody { + return new UserDataBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): UserDataBody { + return new UserDataBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): UserDataBody { + return new UserDataBody().fromJsonString(jsonString, options); + } + + static equals(a: UserDataBody | PlainMessage | undefined, b: UserDataBody | PlainMessage | undefined): boolean { + return proto3.util.equals(UserDataBody, a, b); + } +} + +/** + * @generated from message Embed + */ +export class Embed extends Message$1 { + /** + * @generated from oneof Embed.embed + */ + embed: { + /** + * @generated from field: string url = 1; + */ + value: string; + case: "url"; + } | { + /** + * @generated from field: CastId cast_id = 2; + */ + value: CastId; + case: "castId"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "Embed"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "url", kind: "scalar", T: 9 /* ScalarType.STRING */, oneof: "embed" }, + { no: 2, name: "cast_id", kind: "message", T: CastId, oneof: "embed" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Embed { + return new Embed().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Embed { + return new Embed().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Embed { + return new Embed().fromJsonString(jsonString, options); + } + + static equals(a: Embed | PlainMessage | undefined, b: Embed | PlainMessage | undefined): boolean { + return proto3.util.equals(Embed, a, b); + } +} + +/** + * * Adds a new Cast + * + * @generated from message CastAddBody + */ +export class CastAddBody extends Message$1 { + /** + * URLs to be embedded in the cast + * + * @generated from field: repeated string embeds_deprecated = 1; + */ + embedsDeprecated: string[] = []; + + /** + * Fids mentioned in the cast + * + * @generated from field: repeated uint64 mentions = 2; + */ + mentions: bigint[] = []; + + /** + * @generated from oneof CastAddBody.parent + */ + parent: { + /** + * Parent cast of the cast + * + * @generated from field: CastId parent_cast_id = 3; + */ + value: CastId; + case: "parentCastId"; + } | { + /** + * Parent URL + * + * @generated from field: string parent_url = 7; + */ + value: string; + case: "parentUrl"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + /** + * Text of the cast + * + * @generated from field: string text = 4; + */ + text = ""; + + /** + * Positions of the mentions in the text + * + * @generated from field: repeated uint32 mentions_positions = 5; + */ + mentionsPositions: number[] = []; + + /** + * URLs or cast ids to be embedded in the cast + * + * @generated from field: repeated Embed embeds = 6; + */ + embeds: Embed[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "CastAddBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "embeds_deprecated", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 2, name: "mentions", kind: "scalar", T: 4 /* ScalarType.UINT64 */, repeated: true }, + { no: 3, name: "parent_cast_id", kind: "message", T: CastId, oneof: "parent" }, + { no: 7, name: "parent_url", kind: "scalar", T: 9 /* ScalarType.STRING */, oneof: "parent" }, + { no: 4, name: "text", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "mentions_positions", kind: "scalar", T: 13 /* ScalarType.UINT32 */, repeated: true }, + { no: 6, name: "embeds", kind: "message", T: Embed, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): CastAddBody { + return new CastAddBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): CastAddBody { + return new CastAddBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): CastAddBody { + return new CastAddBody().fromJsonString(jsonString, options); + } + + static equals(a: CastAddBody | PlainMessage | undefined, b: CastAddBody | PlainMessage | undefined): boolean { + return proto3.util.equals(CastAddBody, a, b); + } +} + +/** + * * Removes an existing Cast + * + * @generated from message CastRemoveBody + */ +export class CastRemoveBody extends Message$1 { + /** + * Hash of the cast to remove + * + * @generated from field: bytes target_hash = 1; + */ + targetHash = new Uint8Array(0); + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "CastRemoveBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "target_hash", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): CastRemoveBody { + return new CastRemoveBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): CastRemoveBody { + return new CastRemoveBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): CastRemoveBody { + return new CastRemoveBody().fromJsonString(jsonString, options); + } + + static equals(a: CastRemoveBody | PlainMessage | undefined, b: CastRemoveBody | PlainMessage | undefined): boolean { + return proto3.util.equals(CastRemoveBody, a, b); + } +} + +/** + * * Identifier used to look up a Cast + * + * @generated from message CastId + */ +export class CastId extends Message$1 { + /** + * Fid of the user who created the cast + * + * @generated from field: uint64 fid = 1; + */ + fid = protoInt64.zero; + + /** + * Hash of the cast + * + * @generated from field: bytes hash = 2; + */ + hash = new Uint8Array(0); + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "CastId"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "fid", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, + { no: 2, name: "hash", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): CastId { + return new CastId().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): CastId { + return new CastId().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): CastId { + return new CastId().fromJsonString(jsonString, options); + } + + static equals(a: CastId | PlainMessage | undefined, b: CastId | PlainMessage | undefined): boolean { + return proto3.util.equals(CastId, a, b); + } +} + +/** + * * Adds or removes a Reaction from a Cast + * + * @generated from message ReactionBody + */ +export class ReactionBody extends Message$1 { + /** + * Type of reaction + * + * @generated from field: ReactionType type = 1; + */ + type = ReactionType.NONE; + + /** + * @generated from oneof ReactionBody.target + */ + target: { + /** + * CastId of the Cast to react to + * + * @generated from field: CastId target_cast_id = 2; + */ + value: CastId; + case: "targetCastId"; + } | { + /** + * URL to react to + * + * @generated from field: string target_url = 3; + */ + value: string; + case: "targetUrl"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "ReactionBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "type", kind: "enum", T: proto3.getEnumType(ReactionType) }, + { no: 2, name: "target_cast_id", kind: "message", T: CastId, oneof: "target" }, + { no: 3, name: "target_url", kind: "scalar", T: 9 /* ScalarType.STRING */, oneof: "target" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReactionBody { + return new ReactionBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReactionBody { + return new ReactionBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReactionBody { + return new ReactionBody().fromJsonString(jsonString, options); + } + + static equals(a: ReactionBody | PlainMessage | undefined, b: ReactionBody | PlainMessage | undefined): boolean { + return proto3.util.equals(ReactionBody, a, b); + } +} + +/** + * * Adds a Verification of ownership of an Address based on Protocol + * + * @generated from message VerificationAddAddressBody + */ +export class VerificationAddAddressBody extends Message$1 { + /** + * Address being verified for a given Protocol + * + * @generated from field: bytes address = 1; + */ + address = new Uint8Array(0); + + /** + * Signature produced by the user's address for a given Protocol + * + * @generated from field: bytes claim_signature = 2; + */ + claimSignature = new Uint8Array(0); + + /** + * Hash of the latest Ethereum block when the signature was produced + * + * @generated from field: bytes block_hash = 3; + */ + blockHash = new Uint8Array(0); + + /** + * Type of verification. 0 = EOA, 1 = contract + * + * @generated from field: uint32 verification_type = 4; + */ + verificationType = 0; + + /** + * 0 for EOA verifications, 1 or 10 for contract verifications + * + * @generated from field: uint32 chain_id = 5; + */ + chainId = 0; + + /** + * Protocol of the Verification + * + * @generated from field: Protocol protocol = 7; + */ + protocol = Protocol.ETHEREUM; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "VerificationAddAddressBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "address", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 2, name: "claim_signature", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 3, name: "block_hash", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 4, name: "verification_type", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, + { no: 5, name: "chain_id", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, + { no: 7, name: "protocol", kind: "enum", T: proto3.getEnumType(Protocol) }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): VerificationAddAddressBody { + return new VerificationAddAddressBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): VerificationAddAddressBody { + return new VerificationAddAddressBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): VerificationAddAddressBody { + return new VerificationAddAddressBody().fromJsonString(jsonString, options); + } + + static equals(a: VerificationAddAddressBody | PlainMessage | undefined, b: VerificationAddAddressBody | PlainMessage | undefined): boolean { + return proto3.util.equals(VerificationAddAddressBody, a, b); + } +} + +/** + * * Removes a Verification of a given protocol + * + * @generated from message VerificationRemoveBody + */ +export class VerificationRemoveBody extends Message$1 { + /** + * Address of the Verification to remove + * + * @generated from field: bytes address = 1; + */ + address = new Uint8Array(0); + + /** + * Protocol of the Verification to remove + * + * @generated from field: Protocol protocol = 2; + */ + protocol = Protocol.ETHEREUM; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "VerificationRemoveBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "address", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 2, name: "protocol", kind: "enum", T: proto3.getEnumType(Protocol) }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): VerificationRemoveBody { + return new VerificationRemoveBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): VerificationRemoveBody { + return new VerificationRemoveBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): VerificationRemoveBody { + return new VerificationRemoveBody().fromJsonString(jsonString, options); + } + + static equals(a: VerificationRemoveBody | PlainMessage | undefined, b: VerificationRemoveBody | PlainMessage | undefined): boolean { + return proto3.util.equals(VerificationRemoveBody, a, b); + } +} + +/** + * * Adds or removes a Link + * + * @generated from message LinkBody + */ +export class LinkBody extends Message$1 { + /** + * Type of link, <= 8 characters + * + * @generated from field: string type = 1; + */ + type = ""; + + /** + * User-defined timestamp that preserves original timestamp when message.data.timestamp needs to be updated for compaction + * + * @generated from field: optional uint32 displayTimestamp = 2; + */ + displayTimestamp?: number; + + /** + * @generated from oneof LinkBody.target + */ + target: { + /** + * The fid the link relates to + * + * @generated from field: uint64 target_fid = 3; + */ + value: bigint; + case: "targetFid"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "LinkBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "type", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "displayTimestamp", kind: "scalar", T: 13 /* ScalarType.UINT32 */, opt: true }, + { no: 3, name: "target_fid", kind: "scalar", T: 4 /* ScalarType.UINT64 */, oneof: "target" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): LinkBody { + return new LinkBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): LinkBody { + return new LinkBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): LinkBody { + return new LinkBody().fromJsonString(jsonString, options); + } + + static equals(a: LinkBody | PlainMessage | undefined, b: LinkBody | PlainMessage | undefined): boolean { + return proto3.util.equals(LinkBody, a, b); + } +} + +/** + * * A Farcaster Frame action + * + * @generated from message FrameActionBody + */ +export class FrameActionBody extends Message$1 { + /** + * URL of the Frame triggering the action + * + * @generated from field: bytes url = 1; + */ + url = new Uint8Array(0); + + /** + * The index of the button pressed (1-4) + * + * @generated from field: uint32 button_index = 2; + */ + buttonIndex = 0; + + /** + * The cast which contained the frame url + * + * @generated from field: CastId cast_id = 3; + */ + castId?: CastId; + + /** + * Text input from the user, if present + * + * @generated from field: bytes input_text = 4; + */ + inputText = new Uint8Array(0); + + /** + * Serialized frame state value + * + * @generated from field: bytes state = 5; + */ + state = new Uint8Array(0); + + /** + * Chain-specific transaction ID for tx actions + * + * @generated from field: bytes transaction_id = 6; + */ + transactionId = new Uint8Array(0); + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "FrameActionBody"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "url", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 2, name: "button_index", kind: "scalar", T: 13 /* ScalarType.UINT32 */ }, + { no: 3, name: "cast_id", kind: "message", T: CastId }, + { no: 4, name: "input_text", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 5, name: "state", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 6, name: "transaction_id", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): FrameActionBody { + return new FrameActionBody().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): FrameActionBody { + return new FrameActionBody().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): FrameActionBody { + return new FrameActionBody().fromJsonString(jsonString, options); + } + + static equals(a: FrameActionBody | PlainMessage | undefined, b: FrameActionBody | PlainMessage | undefined): boolean { + return proto3.util.equals(FrameActionBody, a, b); + } +} + diff --git a/src/protobufs/generated/username_proof_pb.ts b/src/protobufs/generated/username_proof_pb.ts new file mode 100644 index 00000000..070f2c43 --- /dev/null +++ b/src/protobufs/generated/username_proof_pb.ts @@ -0,0 +1,101 @@ +// @generated by protoc-gen-es v1.7.2 with parameter "target=ts" +// @generated from file username_proof.proto (syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; + +/** + * @generated from enum UserNameType + */ +export enum UserNameType { + /** + * @generated from enum value: USERNAME_TYPE_NONE = 0; + */ + USERNAME_TYPE_NONE = 0, + + /** + * @generated from enum value: USERNAME_TYPE_FNAME = 1; + */ + USERNAME_TYPE_FNAME = 1, + + /** + * @generated from enum value: USERNAME_TYPE_ENS_L1 = 2; + */ + USERNAME_TYPE_ENS_L1 = 2, +} +// Retrieve enum metadata with: proto3.getEnumType(UserNameType) +proto3.util.setEnumType(UserNameType, "UserNameType", [ + { no: 0, name: "USERNAME_TYPE_NONE" }, + { no: 1, name: "USERNAME_TYPE_FNAME" }, + { no: 2, name: "USERNAME_TYPE_ENS_L1" }, +]); + +/** + * @generated from message UserNameProof + */ +export class UserNameProof extends Message { + /** + * @generated from field: uint64 timestamp = 1; + */ + timestamp = protoInt64.zero; + + /** + * @generated from field: bytes name = 2; + */ + name = new Uint8Array(0); + + /** + * @generated from field: bytes owner = 3; + */ + owner = new Uint8Array(0); + + /** + * @generated from field: bytes signature = 4; + */ + signature = new Uint8Array(0); + + /** + * @generated from field: uint64 fid = 5; + */ + fid = protoInt64.zero; + + /** + * @generated from field: UserNameType type = 6; + */ + type = UserNameType.USERNAME_TYPE_NONE; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "UserNameProof"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "timestamp", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, + { no: 2, name: "name", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 3, name: "owner", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 4, name: "signature", kind: "scalar", T: 12 /* ScalarType.BYTES */ }, + { no: 5, name: "fid", kind: "scalar", T: 4 /* ScalarType.UINT64 */ }, + { no: 6, name: "type", kind: "enum", T: proto3.getEnumType(UserNameType) }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): UserNameProof { + return new UserNameProof().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): UserNameProof { + return new UserNameProof().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): UserNameProof { + return new UserNameProof().fromJsonString(jsonString, options); + } + + static equals(a: UserNameProof | PlainMessage | undefined, b: UserNameProof | PlainMessage | undefined): boolean { + return proto3.util.equals(UserNameProof, a, b); + } +} + diff --git a/src/utils/requestToContext.ts b/src/utils/requestToContext.ts index 0ff07193..79fada8c 100644 --- a/src/utils/requestToContext.ts +++ b/src/utils/requestToContext.ts @@ -43,20 +43,20 @@ export async function requestToContext( return {} as any })() - const verified = await (async () => { - if (verify === false) return false - if (!trustedData) return false - if (!hubApiUrl) return false + const trustedFrameData = await (async () => { + if (verify === false) return null + if (!trustedData) return null + if (!hubApiUrl) return null try { - await verifyFrame({ + const { frameData } = await verifyFrame({ hubApiUrl, frameUrl: untrustedData.url, trustedData, url: req.url, }) - return true + return { ...frameData, state: frameData.state || untrustedData.state } } catch (err) { - if (verify === 'silent') return false + if (verify === 'silent') return null throw err } })() @@ -65,9 +65,9 @@ export async function requestToContext( initialPath: initialPath ? initialPath : new URL(req.url).pathname, previousState, previousButtonValues, - frameData: untrustedData, + frameData: trustedFrameData || untrustedData, status: req.method === 'POST' ? 'response' : 'initial', url: req.url, - verified, + verified: Boolean(trustedFrameData), } } diff --git a/src/utils/verifyFrame.ts b/src/utils/verifyFrame.ts index 39df12ed..72846bd6 100644 --- a/src/utils/verifyFrame.ts +++ b/src/utils/verifyFrame.ts @@ -1,5 +1,6 @@ -import { hexToBytes } from '@noble/curves/abstract/utils' -import { type TrustedData } from '../types.js' +import { bytesToHex, bytesToString, hexToBytes } from 'viem' +import { FrameActionBody, Message } from '../protobufs/generated/message_pb.js' +import { type FrameData, type TrustedData } from '../types.js' import { parsePath } from './parsePath.js' export type VerifyFrameParameters = { @@ -10,14 +11,18 @@ export type VerifyFrameParameters = { url: string } +export type VerifyFrameReturnType = { + frameData: FrameData +} + export async function verifyFrame({ fetchOptions, frameUrl, hubApiUrl, trustedData, url, -}: VerifyFrameParameters): Promise { - const body = hexToBytes(trustedData.messageBytes) +}: VerifyFrameParameters): Promise { + const body = hexToBytes(`0x${trustedData.messageBytes}`) const response = await fetch(`${hubApiUrl}/v1/validateMessage`, { ...fetchOptions, method: 'POST', @@ -27,9 +32,37 @@ export async function verifyFrame({ }, body, }).then((res) => res.json()) + if (!response.valid) throw new Error(`message is invalid. ${response.details}`) if (!parsePath(frameUrl)?.startsWith(parsePath(url))) throw new Error(`Invalid frame url: ${frameUrl}. Expected: ${url}.`) + + const message = Message.fromBinary(body) + const frameData = messageToFrameData(message) + return { frameData } +} + +//////////////////////////////////////////////////////////////////// +// Utilties + +function messageToFrameData(message: Message): FrameData { + const frameActionBody = message.data?.body.value as FrameActionBody + const frameData: FrameData = { + castId: { + fid: Number(frameActionBody.castId?.fid), + hash: bytesToHex(frameActionBody.castId?.hash!), + }, + fid: Number(message.data?.fid!), + messageHash: bytesToHex(message.hash), + network: message.data?.network!, + timestamp: message.data?.timestamp!, + url: bytesToString(frameActionBody.url), + buttonIndex: frameActionBody.buttonIndex as any, + inputText: bytesToString(frameActionBody.inputText), + state: bytesToString(frameActionBody.state), + } + + return frameData }