diff --git a/.changeset/new-dragons-serve.md b/.changeset/new-dragons-serve.md
new file mode 100644
index 0000000000..4a6ad30b9c
--- /dev/null
+++ b/.changeset/new-dragons-serve.md
@@ -0,0 +1,11 @@
+---
+'@coinbase/onchainkit': minor
+---
+
+- **feat**: deprecated `getFrameAccountAddress` as now `getFrameMessage` returns also the Account Address. #60
+- **feat**: integrated with Neynars api to get validated messages + additional context like recast/follow/etc. By @robpolak #59
+- **fix**: removed farcaster references as they were generating build errors and compatibility issues. By @robpolak #59
+
+BREAKING CHANGES
+
+I will write the breaking changes in the next PR
diff --git a/README.md b/README.md
index 3d2d725bfa..e9e152a5e9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
# [OnchainKit](https://github.com/coinbase/onchainkit/)
@@ -32,73 +32,12 @@ Creating a frame is easy: select an image and add clickable buttons. When a butt
Utilities:
-- [getFrameAccountAddress()](https://github.com/coinbase/onchainkit?tab=readme-ov-file#getframeaccountaddressmessage-options): Retrieves the user **Account Address** from a Frame message.
- [getFrameHtmlResponse()](https://github.com/coinbase/onchainkit?tab=readme-ov-file#getframehtmlresponseframemetadata): Retrieves the **Frame HTML** for your HTTP responses.
- [getFrameMessage()](https://github.com/coinbase/onchainkit?tab=readme-ov-file#getframemessageframerequest): Retrieves a valid **Frame message** from the Frame Signature Packet.
- [getFrameMetadata()](https://github.com/coinbase/onchainkit?tab=readme-ov-file#getframeframemetadata): Retrieves valid **Frame metadata** for your initial HTML page.
-### getFrameAccountAddress(message, options)
-
-When a user interacts with your Frame, you will receive a JSON message called the "Frame Signature Packet." Once you validate this `message`, you can extract the Account Address by using the `getFrameAccountAddress(message)` function.
-
-This Account Address can then be utilized for subsequent operations, enhancing the personalized experience of each individual using the Frame.
-
-Note: To utilize this function, we rely on [Neynar APIs](https://docs.neynar.com/reference/user-bulk). In order to avoid rate limiting, please ensure that you have your own API KEY. Sign up [here](https://neynar.com).
-
-```ts
-// Steps 1. import getFrameAccountAddress from @coinbase/onchainkit
-import { FrameRequest, getFrameAccountAddress, getFrameMessage } from '@coinbase/onchainkit';
-import { NextRequest, NextResponse } from 'next/server';
-
-async function getResponse(req: NextRequest): Promise {
- let accountAddress = '';
- // Step 2. Read the body from the Next Request
- const body: FrameRequest = await req.json();
- // Step 3. Validate the message
- const { isValid, message } = await getFrameMessage(body);
-
- // Step 4. Determine the experience based on the validity of the message
- if (isValid) {
- // Step 5. Get from the message the Account Address of the user using the Frame
- accountAddress = await getFrameAccountAddress(message, { NEYNAR_API_KEY: 'NEYNAR_ONCHAIN_KIT' });
- } else {
- // sorry, the message is not valid and it will be undefined
- }
-
- ...
-}
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
-```
-
-**@Param**
-
-```ts
-type AccountAddressRequest = {
- // The validated message from the getFrameMessage() function
- message: FrameData;
- options: {
- // The NEYNAR_API_KEY used to access Neynar Farcaster Indexer
- // https://docs.neynar.com/reference/user-bulk
- NEYNAR_API_KEY: string;
- };
-};
-```
-
-**@Returns**
-
-```ts
-type AccountAddressResponse = Promise;
-```
-
-
-
### getFrameHtmlResponse(frameMetadata)
When you need to send an HTML Frame Response, the `getFrameHtmlResponse` method is here to assist you.
@@ -108,7 +47,6 @@ It generates a valid HTML string response with a frame and utilizes `FrameMetada
```ts
import {
FrameRequest,
- getFrameAccountAddress,
getFrameMessage,
getFrameHtmlResponse,
} from '@coinbase/onchainkit';
diff --git a/docs/logo-v-0-4.png b/docs/logo-v-0-4.png
new file mode 100644
index 0000000000..19049f15af
Binary files /dev/null and b/docs/logo-v-0-4.png differ
diff --git a/src/core/farcasterTypes.ts b/src/core/farcasterTypes.ts
index abfb20f747..c525723f09 100644
--- a/src/core/farcasterTypes.ts
+++ b/src/core/farcasterTypes.ts
@@ -1,15 +1,4 @@
-import { NeynarFrameValidationResponse } from '../utils/neynar/frame/neynarFrameModels';
-
-export interface FrameRequest {
- untrustedData: FrameData;
- trustedData: {
- messageBytes: string;
- };
-}
-
-export type FrameValidationResponse =
- | { isValid: true; message: NeynarFrameValidationResponse }
- | { isValid: false; message: undefined };
+import { NeynarFrameValidationInternalModel } from '../utils/neynar/frame/types';
export interface FrameData {
fid: number;
@@ -24,6 +13,34 @@ export interface FrameData {
};
}
+export interface FrameRequest {
+ untrustedData: FrameData;
+ trustedData: {
+ messageBytes: string;
+ };
+}
+
+/**
+ * Simplified Object model with the raw Neynar data if-needed.
+ */
+export interface FrameValidationData {
+ valid: boolean;
+ button: number;
+ liked: boolean;
+ recasted: boolean;
+ following: boolean;
+ interactor: {
+ fid: number;
+ custody_address: string;
+ verified_accounts: string[];
+ };
+ raw: NeynarFrameValidationInternalModel;
+}
+
+export type FrameValidationResponse =
+ | { isValid: true; message: FrameValidationData }
+ | { isValid: false; message: undefined };
+
export function convertToFrame(json: any) {
return {
fid: json.fid,
diff --git a/src/core/getFrameAccountAddress.test.ts b/src/core/getFrameAccountAddress.test.ts
deleted file mode 100644
index e27a31a762..0000000000
--- a/src/core/getFrameAccountAddress.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { getFrameAccountAddress } from './getFrameAccountAddress';
-import { mockNeynarResponse } from './mock';
-import { neynarBulkUserLookup } from '../utils/neynar/user/neynarUserFunctions';
-import { FrameData } from './farcasterTypes';
-
-jest.mock('../utils/neynar/user/neynarUserFunctions', () => {
- return {
- neynarBulkUserLookup: jest.fn(),
- };
-});
-
-describe('getFrameAccountAddress', () => {
- const fakeMessage = {
- fid: 1234,
- };
- const fakeApiKey = {
- NEYNAR_API_KEY: '1234',
- };
-
- it('should return the first verification for valid input', async () => {
- const addresses = ['0xaddr1'];
- mockNeynarResponse(fakeMessage.fid, addresses, neynarBulkUserLookup as jest.Mock);
-
- const response = await getFrameAccountAddress(fakeMessage as FrameData, fakeApiKey);
- expect(response).toEqual(addresses[0]);
- });
-
- it('should return undefined for invalid input', async () => {
- mockNeynarResponse(fakeMessage.fid, undefined, neynarBulkUserLookup as jest.Mock);
-
- const response = await getFrameAccountAddress(fakeMessage as FrameData, fakeApiKey);
- expect(response).toBeUndefined();
- });
-});
diff --git a/src/core/getFrameAccountAddress.ts b/src/core/getFrameAccountAddress.ts
deleted file mode 100644
index b68ad287a1..0000000000
--- a/src/core/getFrameAccountAddress.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { neynarBulkUserLookup } from '../utils/neynar/user/neynarUserFunctions';
-import { FrameData } from './farcasterTypes';
-
-type AccountAddressResponse = Promise;
-
-type FidResponse = {
- verifications: string[];
-};
-
-/**
- * Get the Account Address from the Farcaster ID using the Frame.
- * This uses a Neynar api to get verified addresses belonging
- * to the user wht that FID.
- *
- * This is using a demo api key so please register
- * on through https://neynar.com/.
- * @param message The validated message from the Frame
- * @param NEYNAR_API_KEY The api key for the Neynar API
- * @returns The account address or undefined
- */
-async function getFrameAccountAddress(
- message: FrameData,
- { NEYNAR_API_KEY = 'NEYNAR_ONCHAIN_KIT' },
-): AccountAddressResponse {
- // Get the Farcaster ID from the message
- const farcasterID = message.fid ?? 0;
- // Get the user verifications from the Farcaster Indexer
- const bulkUserLookupResponse = await neynarBulkUserLookup([farcasterID], NEYNAR_API_KEY);
- if (bulkUserLookupResponse?.users) {
- const userVerifications = bulkUserLookupResponse?.users[0] as FidResponse;
- if (userVerifications.verifications) {
- return userVerifications.verifications[0];
- }
- }
- return;
-}
-
-export { getFrameAccountAddress };
diff --git a/src/index.ts b/src/index.ts
index 6245bb8b20..787820fac9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,9 +1,6 @@
-// 🌲
-const version = '0.3.1';
-
-export { version };
+// 🌲☀️🌲
+export { version } from './version';
export { getFrameHtmlResponse } from './core/getFrameHtmlResponse';
-export { getFrameAccountAddress } from './core/getFrameAccountAddress';
export { getFrameMetadata } from './core/getFrameMetadata';
export { getFrameMessage } from './core/getFrameMessage';
-export type { FrameRequest, FrameData } from './core/farcasterTypes';
+export type { FrameRequest, FrameValidationData } from './core/farcasterTypes';
diff --git a/src/utils/neynar/frame/neynarFrameFunctions.ts b/src/utils/neynar/frame/neynarFrameFunctions.ts
index 82b0e24b4c..1af22c2bf2 100644
--- a/src/utils/neynar/frame/neynarFrameFunctions.ts
+++ b/src/utils/neynar/frame/neynarFrameFunctions.ts
@@ -1,5 +1,7 @@
+import { version } from '../../../version';
+import { FrameValidationData } from '../../../core/farcasterTypes';
import { FetchError } from '../exceptions/FetchError';
-import { convertToNeynarResponseModel, NeynarFrameValidationResponse } from './neynarFrameModels';
+import { convertToNeynarResponseModel } from './neynarFrameModels';
export const NEYNAR_DEFAULT_API_KEY = 'NEYNAR_ONCHAIN_KIT';
@@ -8,11 +10,16 @@ export async function neynarFrameValidation(
apiKey: string = NEYNAR_DEFAULT_API_KEY,
castReactionContext = true,
followContext = true,
-): Promise {
+): Promise {
const options = {
method: 'POST',
url: `https://api.neynar.com/v2/farcaster/frame/validate`,
- headers: { accept: 'application/json', api_key: apiKey, 'content-type': 'application/json' },
+ headers: {
+ accept: 'application/json',
+ api_key: apiKey,
+ 'content-type': 'application/json',
+ onchainkit_version: version,
+ },
body: JSON.stringify({
message_bytes_in_hex: messageBytes,
cast_reaction_context: castReactionContext, // Returns if the user has liked/recasted
diff --git a/src/utils/neynar/frame/neynarFrameModels.ts b/src/utils/neynar/frame/neynarFrameModels.ts
index 5b042d385d..006ab419ff 100644
--- a/src/utils/neynar/frame/neynarFrameModels.ts
+++ b/src/utils/neynar/frame/neynarFrameModels.ts
@@ -1,140 +1,7 @@
-/**
- * Simplified Object model with the raw Neynar data if-needed.
- */
-export interface NeynarFrameValidationResponse {
- valid: boolean;
- button: number;
- liked: boolean;
- recasted: boolean;
- following: boolean;
- interactor: {
- fid: number;
- custody_address: string;
- verified_accounts: string[];
- };
- raw: NeynarFrameValidationInternalModel;
-}
-/**
- * Raw Response from Neynar
- */
-export interface NeynarFrameValidationInternalModel {
- valid: boolean;
- action: {
- object: string;
- interactor: {
- object: string;
- fid: number;
- custody_address: string;
- username: null | string;
- display_name: string;
- pfp_url: string;
- profile: {
- bio: {
- text: string;
- mentioned_profiles?: any[];
- };
- };
- follower_count: number;
- following_count: number;
- verifications: any[];
- active_status: string;
- viewer_context: {
- following: boolean;
- followed_by: boolean;
- };
- };
- tapped_button: {
- index: number;
- };
- cast: {
- object: string;
- hash: string;
- thread_hash: string;
- parent_hash: null | string;
- parent_url: string;
- root_parent_url: string;
- parent_author: {
- fid: null | number;
- };
- author: {
- object: string;
- fid: number;
- custody_address: string;
- username: string;
- display_name: string;
- pfp_url: string;
- profile: {
- bio: {
- text: string;
- mentioned_profiles?: any[];
- };
- };
- follower_count: number;
- following_count: number;
- verifications: any[];
- active_status: string;
- viewer_context: {
- liked: boolean;
- recasted: boolean;
- };
- };
- text: string;
- timestamp: string;
- embeds: {
- url: string;
- }[];
- frames: {
- version: string;
- title: string;
- image: string;
- buttons: {
- index: number;
- title: string;
- action_type: string;
- }[];
- post_url: string;
- frames_url: string;
- }[];
- reactions: {
- likes: {
- fid: number;
- fname: string;
- }[];
- recasts: {
- fid: number;
- fname: string;
- }[];
- };
- replies: {
- count: number;
- };
- mentioned_profiles: {
- object: string;
- fid: number;
- custody_address: string;
- username: string;
- display_name: string;
- pfp_url: string;
- profile: {
- bio: {
- text: string;
- mentioned_profiles?: any[];
- };
- };
- follower_count: number;
- following_count: number;
- verifications: any[];
- active_status: string;
- }[];
- viewer_context: {
- liked: boolean;
- recasted: boolean;
- };
- };
- };
-}
+import { FrameValidationData } from '../../../core/farcasterTypes';
+import { NeynarFrameValidationInternalModel } from './types';
-export function convertToNeynarResponseModel(data: any): NeynarFrameValidationResponse | undefined {
+export function convertToNeynarResponseModel(data: any): FrameValidationData | undefined {
if (!data) {
return;
}
diff --git a/src/utils/neynar/frame/types.ts b/src/utils/neynar/frame/types.ts
new file mode 100644
index 0000000000..f45edbcf36
--- /dev/null
+++ b/src/utils/neynar/frame/types.ts
@@ -0,0 +1,119 @@
+/**
+ * Raw Response from Neynar
+ */
+export interface NeynarFrameValidationInternalModel {
+ valid: boolean;
+ action: {
+ object: string;
+ interactor: {
+ object: string;
+ fid: number;
+ custody_address: string;
+ username: null | string;
+ display_name: string;
+ pfp_url: string;
+ profile: {
+ bio: {
+ text: string;
+ mentioned_profiles?: any[];
+ };
+ };
+ follower_count: number;
+ following_count: number;
+ verifications: any[];
+ active_status: string;
+ viewer_context: {
+ following: boolean;
+ followed_by: boolean;
+ };
+ };
+ tapped_button: {
+ index: number;
+ };
+ cast: {
+ object: string;
+ hash: string;
+ thread_hash: string;
+ parent_hash: null | string;
+ parent_url: string;
+ root_parent_url: string;
+ parent_author: {
+ fid: null | number;
+ };
+ author: {
+ object: string;
+ fid: number;
+ custody_address: string;
+ username: string;
+ display_name: string;
+ pfp_url: string;
+ profile: {
+ bio: {
+ text: string;
+ mentioned_profiles?: any[];
+ };
+ };
+ follower_count: number;
+ following_count: number;
+ verifications: any[];
+ active_status: string;
+ viewer_context: {
+ liked: boolean;
+ recasted: boolean;
+ };
+ };
+ text: string;
+ timestamp: string;
+ embeds: {
+ url: string;
+ }[];
+ frames: {
+ version: string;
+ title: string;
+ image: string;
+ buttons: {
+ index: number;
+ title: string;
+ action_type: string;
+ }[];
+ post_url: string;
+ frames_url: string;
+ }[];
+ reactions: {
+ likes: {
+ fid: number;
+ fname: string;
+ }[];
+ recasts: {
+ fid: number;
+ fname: string;
+ }[];
+ };
+ replies: {
+ count: number;
+ };
+ mentioned_profiles: {
+ object: string;
+ fid: number;
+ custody_address: string;
+ username: string;
+ display_name: string;
+ pfp_url: string;
+ profile: {
+ bio: {
+ text: string;
+ mentioned_profiles?: any[];
+ };
+ };
+ follower_count: number;
+ following_count: number;
+ verifications: any[];
+ active_status: string;
+ }[];
+ viewer_context: {
+ liked: boolean;
+ recasted: boolean;
+ };
+ };
+ };
+}
diff --git a/src/version.ts b/src/version.ts
new file mode 100644
index 0000000000..038b22ba29
--- /dev/null
+++ b/src/version.ts
@@ -0,0 +1 @@
+export const version = '0.4.0';