@farcaster/snap 1.22.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/snap-view-core.js +1 -1
- package/dist/react-native/confetti-overlay.js +1 -1
- package/dist/schemas.d.ts +45 -6
- package/dist/schemas.js +22 -3
- package/dist/server/parseRequest.d.ts +3 -0
- package/dist/server/parseRequest.js +26 -8
- package/dist/server/verify.d.ts +1 -0
- package/dist/server/verify.js +1 -0
- package/package.json +1 -1
- package/src/react/snap-view-core.tsx +1 -1
- package/src/react-native/confetti-overlay.tsx +1 -1
- package/src/schemas.ts +29 -3
- package/src/server/parseRequest.ts +34 -11
- package/src/server/verify.ts +2 -0
- package/src/ui/README.md +8 -8
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { Animated, StyleSheet, View, useWindowDimensions, } from "react-native";
|
|
4
4
|
const CONFETTI_COLORS = [
|
|
5
|
-
"#
|
|
5
|
+
"#907AA9",
|
|
6
6
|
"#EC4899",
|
|
7
7
|
"#3B82F6",
|
|
8
8
|
"#10B981",
|
package/dist/schemas.d.ts
CHANGED
|
@@ -69,11 +69,24 @@ export type SnapHandlerResult = {
|
|
|
69
69
|
ui: SnapSpecInput;
|
|
70
70
|
};
|
|
71
71
|
export declare const payloadSchema: z.ZodObject<{
|
|
72
|
-
fid: z.ZodNumber
|
|
72
|
+
fid: z.ZodOptional<z.ZodNumber>;
|
|
73
73
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
|
|
74
74
|
timestamp: z.ZodNumber;
|
|
75
|
-
nonce: z.ZodString;
|
|
76
75
|
audience: z.ZodString;
|
|
76
|
+
user: z.ZodObject<{
|
|
77
|
+
fid: z.ZodNumber;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
surface: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
80
|
+
type: z.ZodLiteral<"cast">;
|
|
81
|
+
cast: z.ZodObject<{
|
|
82
|
+
hash: z.ZodString;
|
|
83
|
+
author: z.ZodObject<{
|
|
84
|
+
fid: z.ZodNumber;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
88
|
+
type: z.ZodLiteral<"standalone">;
|
|
89
|
+
}, z.core.$strip>], "type">;
|
|
77
90
|
}, z.core.$strip>;
|
|
78
91
|
export type SnapPayload = z.infer<typeof payloadSchema>;
|
|
79
92
|
export declare const ACTION_TYPE_GET: "get";
|
|
@@ -83,22 +96,48 @@ declare const snapGetActionSchema: z.ZodObject<{
|
|
|
83
96
|
}, z.core.$strip>;
|
|
84
97
|
export type SnapGetAction = z.infer<typeof snapGetActionSchema>;
|
|
85
98
|
declare const snapPostActionSchema: z.ZodObject<{
|
|
86
|
-
fid: z.ZodNumber
|
|
99
|
+
fid: z.ZodOptional<z.ZodNumber>;
|
|
87
100
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
|
|
88
101
|
timestamp: z.ZodNumber;
|
|
89
|
-
nonce: z.ZodString;
|
|
90
102
|
audience: z.ZodString;
|
|
103
|
+
user: z.ZodObject<{
|
|
104
|
+
fid: z.ZodNumber;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
surface: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
107
|
+
type: z.ZodLiteral<"cast">;
|
|
108
|
+
cast: z.ZodObject<{
|
|
109
|
+
hash: z.ZodString;
|
|
110
|
+
author: z.ZodObject<{
|
|
111
|
+
fid: z.ZodNumber;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
115
|
+
type: z.ZodLiteral<"standalone">;
|
|
116
|
+
}, z.core.$strip>], "type">;
|
|
91
117
|
type: z.ZodLiteral<"post">;
|
|
92
118
|
}, z.core.$strip>;
|
|
93
119
|
export type SnapPostAction = z.infer<typeof snapPostActionSchema>;
|
|
94
120
|
export declare const snapActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
95
121
|
type: z.ZodLiteral<"get">;
|
|
96
122
|
}, z.core.$strip>, z.ZodObject<{
|
|
97
|
-
fid: z.ZodNumber
|
|
123
|
+
fid: z.ZodOptional<z.ZodNumber>;
|
|
98
124
|
inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
|
|
99
125
|
timestamp: z.ZodNumber;
|
|
100
|
-
nonce: z.ZodString;
|
|
101
126
|
audience: z.ZodString;
|
|
127
|
+
user: z.ZodObject<{
|
|
128
|
+
fid: z.ZodNumber;
|
|
129
|
+
}, z.core.$strip>;
|
|
130
|
+
surface: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
131
|
+
type: z.ZodLiteral<"cast">;
|
|
132
|
+
cast: z.ZodObject<{
|
|
133
|
+
hash: z.ZodString;
|
|
134
|
+
author: z.ZodObject<{
|
|
135
|
+
fid: z.ZodNumber;
|
|
136
|
+
}, z.core.$strip>;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
139
|
+
type: z.ZodLiteral<"standalone">;
|
|
140
|
+
}, z.core.$strip>], "type">;
|
|
102
141
|
type: z.ZodLiteral<"post">;
|
|
103
142
|
}, z.core.$strip>], "type">;
|
|
104
143
|
export type SnapAction = z.infer<typeof snapActionSchema>;
|
package/dist/schemas.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { EFFECT_VALUES, SUPPORTED_SPEC_VERSIONS } from "./constants.js";
|
|
2
|
+
import { EFFECT_VALUES, SUPPORTED_SPEC_VERSIONS, } from "./constants.js";
|
|
3
3
|
import { DEFAULT_THEME_ACCENT, PALETTE_COLOR_VALUES } from "./colors.js";
|
|
4
4
|
// ─── Theme ─────────────────────────────────────────────
|
|
5
5
|
const themeAccentSchema = z.enum(PALETTE_COLOR_VALUES, {
|
|
@@ -31,13 +31,32 @@ const postInputValueSchema = z.union([
|
|
|
31
31
|
z.boolean(),
|
|
32
32
|
z.array(z.string()),
|
|
33
33
|
]);
|
|
34
|
+
const standaloneSurfaceSchema = z.object({
|
|
35
|
+
type: z.literal("standalone"),
|
|
36
|
+
});
|
|
37
|
+
const castSurfaceSchema = z.object({
|
|
38
|
+
type: z.literal("cast"),
|
|
39
|
+
cast: z.object({
|
|
40
|
+
hash: z.string(),
|
|
41
|
+
author: z.object({
|
|
42
|
+
fid: z.number().int().nonnegative(),
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
const surfaceSchema = z.discriminatedUnion("type", [
|
|
47
|
+
castSurfaceSchema,
|
|
48
|
+
standaloneSurfaceSchema,
|
|
49
|
+
]);
|
|
34
50
|
export const payloadSchema = z
|
|
35
51
|
.object({
|
|
36
|
-
fid: z.number().int().nonnegative(),
|
|
52
|
+
fid: z.number().int().nonnegative().optional(), // deprecated in favor of user.fid
|
|
37
53
|
inputs: z.record(z.string(), postInputValueSchema).default({}),
|
|
38
54
|
timestamp: z.number().int(),
|
|
39
|
-
nonce: z.string(),
|
|
40
55
|
audience: z.string(),
|
|
56
|
+
user: z.object({
|
|
57
|
+
fid: z.number().int().nonnegative(),
|
|
58
|
+
}),
|
|
59
|
+
surface: surfaceSchema,
|
|
41
60
|
})
|
|
42
61
|
.strip();
|
|
43
62
|
export const ACTION_TYPE_GET = "get";
|
|
@@ -51,6 +51,14 @@ export async function parseRequest(request, options = {}) {
|
|
|
51
51
|
error: { type: "invalid_json", message: parsed.error.message },
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
const payloadParsed = payloadSchema.safeParse(decodePayload(parsed.data.payload));
|
|
55
|
+
if (!payloadParsed.success) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
error: { type: "validation", issues: payloadParsed.error.issues },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const body = payloadParsed.data;
|
|
54
62
|
if (!options.skipJFSVerification) {
|
|
55
63
|
const jfs = await verifyJFSRequestBody(parsed.data);
|
|
56
64
|
if (!jfs.valid) {
|
|
@@ -59,15 +67,16 @@ export async function parseRequest(request, options = {}) {
|
|
|
59
67
|
error: { type: "signature", message: jfs.error.message },
|
|
60
68
|
};
|
|
61
69
|
}
|
|
70
|
+
if (jfs.signingUserFid !== body.user.fid) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: {
|
|
74
|
+
type: "fid_mismatch",
|
|
75
|
+
message: `JFS header fid "${jfs.signingUserFid}" does not match user.fid "${body.user.fid}"`,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
62
79
|
}
|
|
63
|
-
const payloadParsed = payloadSchema.safeParse(decodePayload(parsed.data.payload));
|
|
64
|
-
if (!payloadParsed.success) {
|
|
65
|
-
return {
|
|
66
|
-
success: false,
|
|
67
|
-
error: { type: "validation", issues: payloadParsed.error.issues },
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
const body = payloadParsed.data;
|
|
71
80
|
if (Math.abs(nowSec - body.timestamp) > maxSkew) {
|
|
72
81
|
return {
|
|
73
82
|
success: false,
|
|
@@ -100,6 +109,15 @@ export async function parseRequest(request, options = {}) {
|
|
|
100
109
|
},
|
|
101
110
|
};
|
|
102
111
|
}
|
|
112
|
+
if (body.fid !== undefined && body.fid !== body.user.fid) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: {
|
|
116
|
+
type: "fid_mismatch",
|
|
117
|
+
message: `fid "${body.fid}" does not match user.fid "${body.user.fid}"`,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
103
121
|
return {
|
|
104
122
|
success: true,
|
|
105
123
|
action: {
|
package/dist/server/verify.d.ts
CHANGED
package/dist/server/verify.js
CHANGED
package/package.json
CHANGED
package/src/schemas.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { Spec } from "@json-render/core";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
EFFECT_VALUES,
|
|
5
|
+
SUPPORTED_SPEC_VERSIONS,
|
|
6
|
+
type SpecVersion,
|
|
7
|
+
} from "./constants";
|
|
4
8
|
import { DEFAULT_THEME_ACCENT, PALETTE_COLOR_VALUES } from "./colors";
|
|
5
9
|
|
|
6
10
|
// ─── Theme ─────────────────────────────────────────────
|
|
@@ -81,13 +85,35 @@ const postInputValueSchema = z.union([
|
|
|
81
85
|
z.array(z.string()),
|
|
82
86
|
]);
|
|
83
87
|
|
|
88
|
+
const standaloneSurfaceSchema = z.object({
|
|
89
|
+
type: z.literal("standalone"),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const castSurfaceSchema = z.object({
|
|
93
|
+
type: z.literal("cast"),
|
|
94
|
+
cast: z.object({
|
|
95
|
+
hash: z.string(),
|
|
96
|
+
author: z.object({
|
|
97
|
+
fid: z.number().int().nonnegative(),
|
|
98
|
+
}),
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const surfaceSchema = z.discriminatedUnion("type", [
|
|
103
|
+
castSurfaceSchema,
|
|
104
|
+
standaloneSurfaceSchema,
|
|
105
|
+
]);
|
|
106
|
+
|
|
84
107
|
export const payloadSchema = z
|
|
85
108
|
.object({
|
|
86
|
-
fid: z.number().int().nonnegative(),
|
|
109
|
+
fid: z.number().int().nonnegative().optional(), // deprecated in favor of user.fid
|
|
87
110
|
inputs: z.record(z.string(), postInputValueSchema).default({}),
|
|
88
111
|
timestamp: z.number().int(),
|
|
89
|
-
nonce: z.string(),
|
|
90
112
|
audience: z.string(),
|
|
113
|
+
user: z.object({
|
|
114
|
+
fid: z.number().int().nonnegative(),
|
|
115
|
+
}),
|
|
116
|
+
surface: surfaceSchema,
|
|
91
117
|
})
|
|
92
118
|
.strip();
|
|
93
119
|
|
|
@@ -33,6 +33,10 @@ export type ParseRequestError =
|
|
|
33
33
|
| {
|
|
34
34
|
type: "origin_mismatch";
|
|
35
35
|
message: string;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
type: "fid_mismatch";
|
|
39
|
+
message: string;
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
export type ParseRequestOptions = {
|
|
@@ -52,7 +56,6 @@ export type ParseRequestOptions = {
|
|
|
52
56
|
* The origin of the request. Derived from the request when not provided.
|
|
53
57
|
*/
|
|
54
58
|
requestOrigin?: string;
|
|
55
|
-
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
export type ParseRequestResult =
|
|
@@ -117,16 +120,6 @@ export async function parseRequest(
|
|
|
117
120
|
};
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
if (!options.skipJFSVerification) {
|
|
121
|
-
const jfs = await verifyJFSRequestBody(parsed.data);
|
|
122
|
-
if (!jfs.valid) {
|
|
123
|
-
return {
|
|
124
|
-
success: false,
|
|
125
|
-
error: { type: "signature", message: jfs.error.message },
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
123
|
const payloadParsed = payloadSchema.safeParse(
|
|
131
124
|
decodePayload(parsed.data.payload),
|
|
132
125
|
);
|
|
@@ -138,6 +131,26 @@ export async function parseRequest(
|
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
const body = payloadParsed.data;
|
|
134
|
+
|
|
135
|
+
if (!options.skipJFSVerification) {
|
|
136
|
+
const jfs = await verifyJFSRequestBody(parsed.data);
|
|
137
|
+
if (!jfs.valid) {
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
error: { type: "signature", message: jfs.error.message },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (jfs.signingUserFid !== body.user.fid) {
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: {
|
|
147
|
+
type: "fid_mismatch",
|
|
148
|
+
message: `JFS header fid "${jfs.signingUserFid}" does not match user.fid "${body.user.fid}"`,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
if (Math.abs(nowSec - body.timestamp) > maxSkew) {
|
|
142
155
|
return {
|
|
143
156
|
success: false,
|
|
@@ -173,6 +186,16 @@ export async function parseRequest(
|
|
|
173
186
|
};
|
|
174
187
|
}
|
|
175
188
|
|
|
189
|
+
if (body.fid !== undefined && body.fid !== body.user.fid) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: {
|
|
193
|
+
type: "fid_mismatch",
|
|
194
|
+
message: `fid "${body.fid}" does not match user.fid "${body.user.fid}"`,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
176
199
|
return {
|
|
177
200
|
success: true,
|
|
178
201
|
action: {
|
package/src/server/verify.ts
CHANGED
|
@@ -27,6 +27,7 @@ export async function verifyJFSRequestBody<TPayload>(
|
|
|
27
27
|
}
|
|
28
28
|
| {
|
|
29
29
|
valid: true;
|
|
30
|
+
signingUserFid: number; // the FID of the user who signed the request
|
|
30
31
|
data: TPayload;
|
|
31
32
|
}
|
|
32
33
|
> {
|
|
@@ -108,6 +109,7 @@ export async function verifyJFSRequestBody<TPayload>(
|
|
|
108
109
|
return {
|
|
109
110
|
valid: true,
|
|
110
111
|
data: payload,
|
|
112
|
+
signingUserFid: header.fid,
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
115
|
|
package/src/ui/README.md
CHANGED
|
@@ -8,14 +8,14 @@ Snaps use a fixed set of named colors called the **palette**:
|
|
|
8
8
|
|
|
9
9
|
| Name | Light hex | Dark hex |
|
|
10
10
|
| -------- | --------- | --------- |
|
|
11
|
-
| `gray` | `#
|
|
12
|
-
| `blue` | `#
|
|
13
|
-
| `red` | `#
|
|
14
|
-
| `amber` | `#
|
|
15
|
-
| `green` | `#
|
|
16
|
-
| `teal` | `#
|
|
17
|
-
| `purple` | `#
|
|
18
|
-
| `pink` | `#
|
|
11
|
+
| `gray` | `#6E6A86` | `#908CAA` |
|
|
12
|
+
| `blue` | `#286983` | `#9CCFD8` |
|
|
13
|
+
| `red` | `#B4637A` | `#EB6F92` |
|
|
14
|
+
| `amber` | `#EA9D34` | `#F6C177` |
|
|
15
|
+
| `green` | `#3E8F8F` | `#56D4A4` |
|
|
16
|
+
| `teal` | `#56949F` | `#3E8FB0` |
|
|
17
|
+
| `purple` | `#907AA9` | `#C4A7E7` |
|
|
18
|
+
| `pink` | `#D7827E` | `#EBBCBA` |
|
|
19
19
|
|
|
20
20
|
These are exported from `@farcaster/snap` as `PALETTE_LIGHT_HEX`, `PALETTE_DARK_HEX`, and the `PaletteColor` type. Clients resolve the correct hex for their current light/dark mode.
|
|
21
21
|
|