Skip to content

Commit 889726a

Browse files
committed
fix: avoid unused api in farcaster frame
1 parent ddf5f93 commit 889726a

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

src/config/farcasterFrame.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { InvalidFrameActionPayloadError, RequestBodyNotJSONError } from '@/constants/error';
2+
import {
3+
ClientProtocolId,
4+
FrameActionDataParsedAndHubContext,
5+
FrameActionPayload,
6+
FrameMessageReturnType,
7+
getAddressesForFid,
8+
getFrameMessage,
9+
HubHttpUrlOptions,
10+
} from 'frames.js';
11+
import { FramesMiddleware, JsonValue } from 'frames.js/core/types';
12+
import { MessageWithWalletAddressImplementation } from 'frames.js/middleware/walletAddressMiddleware';
13+
14+
type FrameMessage = Omit<FrameMessageReturnType<{ fetchHubContext: true }>, 'message'> & {
15+
state?: JsonValue;
16+
} & MessageWithWalletAddressImplementation;
17+
type FramesMessageContext = {
18+
message?: FrameMessage;
19+
clientProtocol?: ClientProtocolId;
20+
};
21+
22+
function isValidFrameActionPayload(value: unknown): value is FrameActionPayload {
23+
return typeof value === 'object' && value !== null && 'trustedData' in value && 'untrustedData' in value;
24+
}
25+
26+
async function decodeFrameActionPayloadFromRequest(request: Request): Promise<FrameActionPayload | undefined> {
27+
try {
28+
// use clone just in case someone wants to read body somewhere along the way
29+
const body = (await request
30+
.clone()
31+
.json()
32+
.catch(() => {
33+
throw new RequestBodyNotJSONError();
34+
})) as JSON;
35+
36+
if (!isValidFrameActionPayload(body)) {
37+
throw new InvalidFrameActionPayloadError();
38+
}
39+
40+
return body;
41+
} catch (e) {
42+
if (e instanceof RequestBodyNotJSONError || e instanceof InvalidFrameActionPayloadError) {
43+
return undefined;
44+
}
45+
46+
// eslint-disable-next-line no-console -- provide feedback to the developer
47+
console.error(e);
48+
49+
return undefined;
50+
}
51+
}
52+
53+
export function farcasterHubContext(options: HubHttpUrlOptions): FramesMiddleware<any, FramesMessageContext> {
54+
return async (context, next) => {
55+
if (context.request.method !== 'POST') {
56+
return next();
57+
}
58+
59+
const payload = await decodeFrameActionPayloadFromRequest(context.request);
60+
if (!payload) {
61+
return next();
62+
}
63+
64+
try {
65+
const message = (await getFrameMessage(payload, {
66+
...options,
67+
fetchHubContext: false,
68+
})) as FrameActionDataParsedAndHubContext;
69+
70+
const requesterEthAddresses = await getAddressesForFid({
71+
fid: message.requesterFid,
72+
options: {
73+
hubHttpUrl: options.hubHttpUrl,
74+
hubRequestOptions: options.hubRequestOptions,
75+
},
76+
});
77+
const requesterCustodyAddress = requesterEthAddresses.find((item) => item.type === 'custody')?.address;
78+
if (!requesterCustodyAddress) {
79+
throw new Error('Custody address not found');
80+
}
81+
82+
const requesterVerifiedAddresses = requesterEthAddresses
83+
.filter((item) => item.type === 'verified')
84+
.map((item) => item.address);
85+
86+
message.requesterVerifiedAddresses = requesterVerifiedAddresses;
87+
message.requesterCustodyAddress = requesterCustodyAddress;
88+
89+
const [address] = message.requesterVerifiedAddresses;
90+
91+
return next({
92+
message: {
93+
...message,
94+
walletAddress() {
95+
return Promise.resolve(address ?? message.requesterCustodyAddress);
96+
},
97+
},
98+
clientProtocol: {
99+
id: 'farcaster',
100+
version: 'vNext', // TODO: Pass version in getFrameMessage
101+
},
102+
});
103+
} catch (error) {
104+
// eslint-disable-next-line no-console -- provide feedback to the developer
105+
console.info(
106+
'farcasterHubContext middleware: could not decode farcaster message from payload, calling next.',
107+
);
108+
return next();
109+
}
110+
};
111+
}

src/config/frames.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { farcasterHubContext } from 'frames.js/middleware';
21
import { imagesWorkerMiddleware } from 'frames.js/middleware/images-worker';
32
import { createFrames } from 'frames.js/next';
43

4+
import { farcasterHubContext } from '@/config/farcasterFrame';
55
import { lensFrame } from '@/config/lensFrame';
66
import { IMAGE_ZOOM_SCALE } from '@/constants';
77
import { env } from '@/constants/env';

src/constants/error.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export class InvalidFrameActionPayloadError extends Error {
2+
constructor(message = 'Invalid frame action payload') {
3+
super(message);
4+
}
5+
}
6+
7+
export class RequestBodyNotJSONError extends Error {
8+
constructor() {
9+
super('Invalid frame action payload, request body is not JSON');
10+
}
11+
}

0 commit comments

Comments
 (0)