Skip to content

Commit

Permalink
fixes: add some additional logic checks and unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
robpolak-cb committed Jan 28, 2024
1 parent 54dac6e commit 51f80fb
Show file tree
Hide file tree
Showing 6 changed files with 2,371 additions and 52 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ pnpm add @coinbase/onchainkit
<br />

## FrameKit 🖼️
A Frame transforms any cast into an interactive app.

A Frame transforms any cast into an interactive app.

Creating a frame is easy: select an image and add clickable buttons. When a button is clicked, you receive a callback and can send another image with more buttons. To learn more, check out "[Farcaster Frames Official Documentation](https://warpcast.notion.site/Farcaster-Frames-4bd47fe97dc74a42a48d3a234636d8c5)".

Expand All @@ -43,7 +44,7 @@ async function getResponse(req: NextRequest): Promise<NextResponse> {
try {
// Step 2. Read the body from the Next Request
const body = await req.json();
// Step 3. Get from the body the Account Address of the user using the Frame
// Step 3. Get from the body the Account Address of the user using the Frame
accountAddress = await getFrameAccountAddress(body, { NEYNAR_API_KEY: 'NEYNAR_API_DOCS' });
} catch (err) {
console.error(err);
Expand Down Expand Up @@ -157,4 +158,4 @@ OnchainKit is all about community; for any questions, feel free to:

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
// ...
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testMatch: ['**/?(*.)+(spec|test).ts'],
};
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@
"format": "prettier --log-level warn --write .",
"format:check": "prettier --check .",
"prebuild": "rimraf dist",
"test": "jest .",
"test:coverage": "jest . --coverage",
"release:check": "changeset status --verbose --since=origin/main",
"release:publish": "yarn install && yarn build && changeset publish",
"release:version": "changeset version && yarn install --immutable"
},
"devDependencies": {
"@changesets/changelog-github": "^0.4.8",
"@changesets/cli": "^2.26.2",
"@types/jest": "^29.5.11",
"jest": "^29.7.0",
"prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.5.9",
"ts-jest": "^29.1.2",
"typescript": "~5.3.3",
"yarn": "^1.22.21"
},
Expand Down
90 changes: 90 additions & 0 deletions src/core/getFrameAccountAddress.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { getFrameAccountAddress } from './getFrameAccountAddress';

jest.mock('@farcaster/hub-nodejs', () => {
return {
getSSLHubRpcClient: jest.fn().mockReturnValue({
validateMessage: jest.fn(),
}),
Message: {
decode: jest.fn(),
},
};
});

function buildFarcasterResponse(fid: number) {
return {
isOk: () => {
return true;
},
value: {
valid: true,
message: {
data: {
fid: fid,
},
},
},
};
}

function mockNeynarResponse(fid: number, addresses: string[]) {
const neynarResponse = {
users: [
{
verifications: addresses,
},
],
};

const getSSLHubRpcClientMock = require('@farcaster/hub-nodejs').getSSLHubRpcClient;
const validateMock = getSSLHubRpcClientMock().validateMessage as jest.Mock;

// Mock the response from the Farcaster hub
validateMock.mockResolvedValue({
isOk: () => true,
value: { valid: true, message: { fid } },
});

validateMock.mockResolvedValue(buildFarcasterResponse(fid));
// Mock the response from Neynar
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(neynarResponse),
}),
) as jest.Mock;
return {
validateMock,
};
}

describe('getFrameAccountAddress', () => {
const fakeFrameData = {
trustedData: {},
};
const fakeApiKey = {
NEYNAR_API_KEY: '1234',
};

it('should return the first verification for valid input', async () => {
const fid = 1234;
const addresses = ['0xaddr1'];
mockNeynarResponse(fid, addresses);

const response = await getFrameAccountAddress(fakeFrameData, fakeApiKey);
expect(response).toEqual(addresses[0]);
});

it('when the call from farcaster fails we should return undefined', async () => {
const fid = 1234;
const addresses = ['0xaddr1'];
const { validateMock } = mockNeynarResponse(fid, addresses);
validateMock.mockClear();
validateMock.mockResolvedValue({
isOk: () => {
return false;
},
});
const response = await getFrameAccountAddress(fakeFrameData, fakeApiKey);
expect(response).toEqual(undefined);
});
});
21 changes: 15 additions & 6 deletions src/core/getFrameAccountAddress.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,43 @@
import { HubRpcClient, Message, getSSLHubRpcClient } from '@farcaster/hub-nodejs';

// URL of the Hub
/**
* Farcaster Hub for signature verification, consider using a private hub if needed:
* https://docs.farcaster.xyz/hubble/hubble
*/
const HUB_URL = 'nemes.farcaster.xyz:2283';
// Create a Hub RPC client
const client: HubRpcClient = getSSLHubRpcClient(HUB_URL);

type FidResponse = {
verifications: string[];
};

function getHubClient(): HubRpcClient {
return getSSLHubRpcClient(HUB_URL);
}
/**
* Get the Account Address from the Farcaster ID using the Frame.
* 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 body
* @param param1
* @returns
*/
async function getFrameAccountAddress(
body: { trustedData?: { messageBytes?: string } },
{ NEYNAR_API_KEY = 'NEYNAR_API_DOCS' },
) {
): Promise<string | undefined> {
let farcasterID = 0;
let validatedMessage: Message | undefined = undefined;
// Get the message from the request body
const frameMessage: Message = Message.decode(
Buffer.from(body?.trustedData?.messageBytes ?? '', 'hex'),
);
// Validate the message
const client = getHubClient();
const result = await client.validateMessage(frameMessage);
if (result.isOk() && result.value.valid && result.value.message) {
validatedMessage = result.value.message;
} else {
return;
}
// Get the Farcaster ID from the message
farcasterID = validatedMessage?.data?.fid ?? 0;
Expand All @@ -47,7 +56,7 @@ async function getFrameAccountAddress(
return userVerifications.verifications[0];
}
}
return '0x00';
return;
}

export { getFrameAccountAddress };
Loading

0 comments on commit 51f80fb

Please sign in to comment.