@farcaster/snap 2.9.0 → 2.10.0

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.
Files changed (37) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/react/catalog-renderer.d.ts +5 -5
  4. package/dist/react/catalog-renderer.js +16 -4
  5. package/dist/react/components/action-button.js +23 -5
  6. package/dist/react/index.d.ts +2 -1
  7. package/dist/react/snap-view-core.js +90 -25
  8. package/dist/react/v1/snap-view.js +1 -1
  9. package/dist/react/v2/snap-view.js +1 -1
  10. package/dist/react-native/components/snap-action-button.js +6 -1
  11. package/dist/react-native/snap-view-core.js +77 -24
  12. package/dist/react-native/types.d.ts +2 -1
  13. package/dist/render-state.d.ts +9 -0
  14. package/dist/render-state.js +27 -0
  15. package/dist/schemas.d.ts +123 -3
  16. package/dist/schemas.js +53 -2
  17. package/dist/server/parseRequest.js +19 -3
  18. package/dist/ui/button.d.ts +1 -0
  19. package/dist/ui/button.js +1 -0
  20. package/dist/ui/catalog.d.ts +13 -0
  21. package/dist/ui/catalog.js +15 -8
  22. package/package.json +1 -1
  23. package/src/index.ts +7 -0
  24. package/src/react/catalog-renderer.tsx +57 -3
  25. package/src/react/components/action-button.tsx +32 -3
  26. package/src/react/index.tsx +4 -1
  27. package/src/react/snap-view-core.tsx +144 -27
  28. package/src/react/v1/snap-view.tsx +1 -0
  29. package/src/react/v2/snap-view.tsx +1 -0
  30. package/src/react-native/components/snap-action-button.tsx +6 -1
  31. package/src/react-native/snap-view-core.tsx +114 -27
  32. package/src/react-native/types.ts +4 -1
  33. package/src/render-state.ts +46 -0
  34. package/src/schemas.ts +73 -2
  35. package/src/server/parseRequest.ts +37 -6
  36. package/src/ui/button.ts +1 -0
  37. package/src/ui/catalog.ts +16 -8
@@ -5,6 +5,15 @@ export type SnapRenderStateChanges = {
5
5
  }[] | Record<string, unknown> | null | undefined;
6
6
  export declare function cloneSnapRenderState<T>(value: T): T;
7
7
  export declare function applyStatePaths(model: Record<string, unknown>, changes: SnapRenderStateChanges): void;
8
+ export declare function buildActionActivityStateChanges({ actionName, params, pending, }: {
9
+ actionName: unknown;
10
+ params: Record<string, unknown>;
11
+ pending: boolean;
12
+ }): {
13
+ path: string;
14
+ value: unknown;
15
+ }[];
16
+ export declare function hasPendingSnapAction(model: SnapRenderState): boolean;
8
17
  export declare function getUnpresentedSnapEffects(model: SnapRenderState, effects: readonly string[] | undefined): string[];
9
18
  export declare function markSnapEffectsPresented(model: SnapRenderState, effects: readonly string[] | undefined): boolean;
10
19
  export declare function buildInitialRenderState({ specState, initialRenderState, themeAccent, }: {
@@ -1,4 +1,5 @@
1
1
  const SNAP_RENDER_STATE_META_KEY = "__snapRender";
2
+ const ACTION_ACTIVITY_KEY_MAX_LENGTH = 64;
2
3
  function isRecord(value) {
3
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
4
5
  }
@@ -75,6 +76,32 @@ export function applyStatePaths(model, changes) {
75
76
  setStateValue(model, parts, value);
76
77
  }
77
78
  }
79
+ function sanitizeActionActivityKey(value) {
80
+ const sanitized = value
81
+ .trim()
82
+ .slice(0, ACTION_ACTIVITY_KEY_MAX_LENGTH)
83
+ .replace(/[^A-Za-z0-9_.-]/g, "_");
84
+ return sanitized || "action";
85
+ }
86
+ function getActionActivityKey(actionName, params) {
87
+ const explicitKey = params.activityKey;
88
+ return sanitizeActionActivityKey(typeof explicitKey === "string" && explicitKey.trim()
89
+ ? explicitKey
90
+ : String(actionName || "action"));
91
+ }
92
+ export function buildActionActivityStateChanges({ actionName, params, pending, }) {
93
+ const key = getActionActivityKey(actionName, params);
94
+ return [
95
+ { path: `/actions/${key}/name`, value: String(actionName || "action") },
96
+ { path: `/actions/${key}/pending`, value: pending },
97
+ ];
98
+ }
99
+ export function hasPendingSnapAction(model) {
100
+ const actions = model.actions;
101
+ if (!isRecord(actions))
102
+ return false;
103
+ return Object.values(actions).some((action) => isRecord(action) && action.pending === true);
104
+ }
78
105
  export function getUnpresentedSnapEffects(model, effects) {
79
106
  const presentedEffects = getPresentedSnapEffects(model);
80
107
  return normalizeEffects(effects).filter((effect) => !presentedEffects.has(effect));
package/dist/schemas.d.ts CHANGED
@@ -69,9 +69,34 @@ export type SnapHandlerResult = {
69
69
  effects?: z.input<typeof snapResponseSchema>["effects"];
70
70
  ui: SnapSpecInput;
71
71
  };
72
+ declare const snapSendTransactionParamsSchema: z.ZodObject<{
73
+ chainId: z.ZodString;
74
+ to: z.ZodString;
75
+ data: z.ZodOptional<z.ZodString>;
76
+ value: z.ZodOptional<z.ZodString>;
77
+ gas: z.ZodOptional<z.ZodString>;
78
+ gasPrice: z.ZodOptional<z.ZodString>;
79
+ maxFeePerGas: z.ZodOptional<z.ZodString>;
80
+ maxPriorityFeePerGas: z.ZodOptional<z.ZodString>;
81
+ }, z.core.$strict>;
82
+ export type SnapSendTransactionParams = z.infer<typeof snapSendTransactionParamsSchema>;
83
+ export declare const snapTransactionResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
84
+ success: z.ZodLiteral<true>;
85
+ transactionHash: z.ZodString;
86
+ }, z.core.$strict>, z.ZodObject<{
87
+ success: z.ZodLiteral<false>;
88
+ reason: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
89
+ unknown: "unknown";
90
+ rejected_by_user: "rejected_by_user";
91
+ failed: "failed";
92
+ }>>>;
93
+ message: z.ZodOptional<z.ZodString>;
94
+ code: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
95
+ transactionHash: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strict>], "success">;
97
+ export type SnapTransactionResult = z.infer<typeof snapTransactionResultSchema>;
72
98
  export declare const payloadSchema: z.ZodObject<{
73
99
  fid: z.ZodOptional<z.ZodNumber>;
74
- inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
75
100
  timestamp: z.ZodNumber;
76
101
  audience: z.ZodString;
77
102
  user: z.ZodObject<{
@@ -88,6 +113,7 @@ export declare const payloadSchema: z.ZodObject<{
88
113
  }, z.core.$strip>, z.ZodObject<{
89
114
  type: z.ZodLiteral<"standalone">;
90
115
  }, z.core.$strip>], "type">;
116
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
91
117
  }, z.core.$strip>;
92
118
  export type SnapPayload = z.infer<typeof payloadSchema>;
93
119
  /** JFS payload shape for POST minus deprecated `fid`; used for GET auth via payload header. */
@@ -112,6 +138,7 @@ export declare const getPayloadSchema: z.ZodObject<{
112
138
  export type SnapGetPayload = z.infer<typeof getPayloadSchema>;
113
139
  export declare const ACTION_TYPE_GET: "get";
114
140
  export declare const ACTION_TYPE_POST: "post";
141
+ export declare const ACTION_TYPE_TRANSACTION_RESULT: "transaction_result";
115
142
  declare const snapGetActionSchema: z.ZodObject<{
116
143
  type: z.ZodLiteral<"get">;
117
144
  user: z.ZodOptional<z.ZodObject<{
@@ -134,7 +161,6 @@ declare const snapGetActionSchema: z.ZodObject<{
134
161
  export type SnapGetAction = z.infer<typeof snapGetActionSchema>;
135
162
  declare const snapPostActionSchema: z.ZodObject<{
136
163
  fid: z.ZodOptional<z.ZodNumber>;
137
- inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
138
164
  timestamp: z.ZodNumber;
139
165
  audience: z.ZodString;
140
166
  user: z.ZodObject<{
@@ -151,9 +177,58 @@ declare const snapPostActionSchema: z.ZodObject<{
151
177
  }, z.core.$strip>, z.ZodObject<{
152
178
  type: z.ZodLiteral<"standalone">;
153
179
  }, z.core.$strip>], "type">;
180
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
154
181
  type: z.ZodLiteral<"post">;
155
182
  }, z.core.$strip>;
156
183
  export type SnapPostAction = z.infer<typeof snapPostActionSchema>;
184
+ export declare const transactionResultPayloadSchema: z.ZodObject<{
185
+ fid: z.ZodOptional<z.ZodNumber>;
186
+ timestamp: z.ZodNumber;
187
+ audience: z.ZodString;
188
+ user: z.ZodObject<{
189
+ fid: z.ZodNumber;
190
+ }, z.core.$strip>;
191
+ surface: z.ZodDiscriminatedUnion<[z.ZodObject<{
192
+ type: z.ZodLiteral<"cast">;
193
+ cast: z.ZodObject<{
194
+ hash: z.ZodString;
195
+ author: z.ZodObject<{
196
+ fid: z.ZodNumber;
197
+ }, z.core.$strip>;
198
+ }, z.core.$strip>;
199
+ }, z.core.$strip>, z.ZodObject<{
200
+ type: z.ZodLiteral<"standalone">;
201
+ }, z.core.$strip>], "type">;
202
+ type: z.ZodLiteral<"transaction_result">;
203
+ transaction: z.ZodObject<{
204
+ request: z.ZodObject<{
205
+ chainId: z.ZodString;
206
+ to: z.ZodString;
207
+ data: z.ZodOptional<z.ZodString>;
208
+ value: z.ZodOptional<z.ZodString>;
209
+ gas: z.ZodOptional<z.ZodString>;
210
+ gasPrice: z.ZodOptional<z.ZodString>;
211
+ maxFeePerGas: z.ZodOptional<z.ZodString>;
212
+ maxPriorityFeePerGas: z.ZodOptional<z.ZodString>;
213
+ }, z.core.$strict>;
214
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
215
+ success: z.ZodLiteral<true>;
216
+ transactionHash: z.ZodString;
217
+ }, z.core.$strict>, z.ZodObject<{
218
+ success: z.ZodLiteral<false>;
219
+ reason: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
220
+ unknown: "unknown";
221
+ rejected_by_user: "rejected_by_user";
222
+ failed: "failed";
223
+ }>>>;
224
+ message: z.ZodOptional<z.ZodString>;
225
+ code: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
226
+ transactionHash: z.ZodOptional<z.ZodString>;
227
+ }, z.core.$strict>], "success">;
228
+ }, z.core.$strict>;
229
+ }, z.core.$strip>;
230
+ export type SnapTransactionResultPayload = z.infer<typeof transactionResultPayloadSchema>;
231
+ export type SnapTransactionResultAction = SnapTransactionResultPayload;
157
232
  export declare const snapActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
158
233
  type: z.ZodLiteral<"get">;
159
234
  user: z.ZodOptional<z.ZodObject<{
@@ -174,7 +249,6 @@ export declare const snapActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
174
249
  }, z.core.$strip>], "type">>;
175
250
  }, z.core.$strip>, z.ZodObject<{
176
251
  fid: z.ZodOptional<z.ZodNumber>;
177
- inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
178
252
  timestamp: z.ZodNumber;
179
253
  audience: z.ZodString;
180
254
  user: z.ZodObject<{
@@ -191,7 +265,53 @@ export declare const snapActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
191
265
  }, z.core.$strip>, z.ZodObject<{
192
266
  type: z.ZodLiteral<"standalone">;
193
267
  }, z.core.$strip>], "type">;
268
+ inputs: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodString>]>>>;
194
269
  type: z.ZodLiteral<"post">;
270
+ }, z.core.$strip>, z.ZodObject<{
271
+ fid: z.ZodOptional<z.ZodNumber>;
272
+ timestamp: z.ZodNumber;
273
+ audience: z.ZodString;
274
+ user: z.ZodObject<{
275
+ fid: z.ZodNumber;
276
+ }, z.core.$strip>;
277
+ surface: z.ZodDiscriminatedUnion<[z.ZodObject<{
278
+ type: z.ZodLiteral<"cast">;
279
+ cast: z.ZodObject<{
280
+ hash: z.ZodString;
281
+ author: z.ZodObject<{
282
+ fid: z.ZodNumber;
283
+ }, z.core.$strip>;
284
+ }, z.core.$strip>;
285
+ }, z.core.$strip>, z.ZodObject<{
286
+ type: z.ZodLiteral<"standalone">;
287
+ }, z.core.$strip>], "type">;
288
+ type: z.ZodLiteral<"transaction_result">;
289
+ transaction: z.ZodObject<{
290
+ request: z.ZodObject<{
291
+ chainId: z.ZodString;
292
+ to: z.ZodString;
293
+ data: z.ZodOptional<z.ZodString>;
294
+ value: z.ZodOptional<z.ZodString>;
295
+ gas: z.ZodOptional<z.ZodString>;
296
+ gasPrice: z.ZodOptional<z.ZodString>;
297
+ maxFeePerGas: z.ZodOptional<z.ZodString>;
298
+ maxPriorityFeePerGas: z.ZodOptional<z.ZodString>;
299
+ }, z.core.$strict>;
300
+ result: z.ZodDiscriminatedUnion<[z.ZodObject<{
301
+ success: z.ZodLiteral<true>;
302
+ transactionHash: z.ZodString;
303
+ }, z.core.$strict>, z.ZodObject<{
304
+ success: z.ZodLiteral<false>;
305
+ reason: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
306
+ unknown: "unknown";
307
+ rejected_by_user: "rejected_by_user";
308
+ failed: "failed";
309
+ }>>>;
310
+ message: z.ZodOptional<z.ZodString>;
311
+ code: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
312
+ transactionHash: z.ZodOptional<z.ZodString>;
313
+ }, z.core.$strict>], "success">;
314
+ }, z.core.$strict>;
195
315
  }, z.core.$strip>], "type">;
196
316
  export type SnapAction = z.infer<typeof snapActionSchema>;
197
317
  export type SnapContext = {
package/dist/schemas.js CHANGED
@@ -49,20 +49,59 @@ const surfaceSchema = z.discriminatedUnion("type", [
49
49
  ]);
50
50
  const fidSchema = z.number().int().nonnegative();
51
51
  const userSchema = z.object({ fid: fidSchema });
52
- export const payloadSchema = z
52
+ const basePayloadSchema = z
53
53
  .object({
54
54
  fid: fidSchema.optional(), // deprecated in favor of user.fid
55
- inputs: z.record(z.string(), postInputValueSchema).default({}),
56
55
  timestamp: z.number().int(),
57
56
  audience: z.string(),
58
57
  user: userSchema,
59
58
  surface: surfaceSchema,
60
59
  })
61
60
  .strip();
61
+ const snapSendTransactionParamsSchema = z
62
+ .object({
63
+ chainId: z.string(),
64
+ to: z.string(),
65
+ data: z.string().optional(),
66
+ value: z.string().optional(),
67
+ gas: z.string().optional(),
68
+ gasPrice: z.string().optional(),
69
+ maxFeePerGas: z.string().optional(),
70
+ maxPriorityFeePerGas: z.string().optional(),
71
+ })
72
+ .strict();
73
+ const snapTransactionSuccessSchema = z
74
+ .object({
75
+ success: z.literal(true),
76
+ transactionHash: z.string(),
77
+ })
78
+ .strict();
79
+ const snapTransactionFailureSchema = z
80
+ .object({
81
+ success: z.literal(false),
82
+ reason: z
83
+ .enum(["rejected_by_user", "failed", "unknown"])
84
+ .optional()
85
+ .default("unknown"),
86
+ message: z.string().optional(),
87
+ code: z.union([z.string(), z.number()]).optional(),
88
+ transactionHash: z.string().optional(),
89
+ })
90
+ .strict();
91
+ export const snapTransactionResultSchema = z.discriminatedUnion("success", [
92
+ snapTransactionSuccessSchema,
93
+ snapTransactionFailureSchema,
94
+ ]);
95
+ export const payloadSchema = basePayloadSchema
96
+ .extend({
97
+ inputs: z.record(z.string(), postInputValueSchema).default({}),
98
+ })
99
+ .strip();
62
100
  /** JFS payload shape for POST minus deprecated `fid`; used for GET auth via payload header. */
63
101
  export const getPayloadSchema = payloadSchema.omit({ inputs: true, fid: true });
64
102
  export const ACTION_TYPE_GET = "get";
65
103
  export const ACTION_TYPE_POST = "post";
104
+ export const ACTION_TYPE_TRANSACTION_RESULT = "transaction_result";
66
105
  const snapGetActionSchema = z.object({
67
106
  type: z.literal(ACTION_TYPE_GET),
68
107
  user: userSchema.optional(),
@@ -73,7 +112,19 @@ const snapGetActionSchema = z.object({
73
112
  const snapPostActionSchema = payloadSchema.extend({
74
113
  type: z.literal(ACTION_TYPE_POST),
75
114
  });
115
+ export const transactionResultPayloadSchema = basePayloadSchema
116
+ .extend({
117
+ type: z.literal(ACTION_TYPE_TRANSACTION_RESULT),
118
+ transaction: z
119
+ .object({
120
+ request: snapSendTransactionParamsSchema,
121
+ result: snapTransactionResultSchema,
122
+ })
123
+ .strict(),
124
+ })
125
+ .strip();
76
126
  export const snapActionSchema = z.discriminatedUnion("type", [
77
127
  snapGetActionSchema,
78
128
  snapPostActionSchema,
129
+ transactionResultPayloadSchema,
79
130
  ]);
@@ -1,4 +1,4 @@
1
- import { ACTION_TYPE_GET, ACTION_TYPE_POST, getPayloadSchema, payloadSchema, } from "../schemas.js";
1
+ import { ACTION_TYPE_TRANSACTION_RESULT, ACTION_TYPE_GET, ACTION_TYPE_POST, getPayloadSchema, payloadSchema, transactionResultPayloadSchema, } from "../schemas.js";
2
2
  import { decodePayload, parseJfs, verifyJFS } from "./verify.js";
3
3
  import { SNAP_PAYLOAD_HEADER } from "../constants.js";
4
4
  const DEFAULT_SNAP_POST_MAX_SKEW_SECONDS = 300;
@@ -46,7 +46,9 @@ async function parseGetRequest(request, options) {
46
46
  async function parsePostRequest(request, options) {
47
47
  const result = await validateJfsPayload({
48
48
  jfsText: await request.text(),
49
- schema: payloadSchema,
49
+ schema: (decodedPayload) => isTransactionResultPayload(decodedPayload)
50
+ ? transactionResultPayloadSchema
51
+ : payloadSchema,
50
52
  request,
51
53
  options,
52
54
  });
@@ -63,11 +65,23 @@ async function parsePostRequest(request, options) {
63
65
  },
64
66
  };
65
67
  }
68
+ if (isTransactionResultPayload(payload)) {
69
+ return {
70
+ success: true,
71
+ action: payload,
72
+ };
73
+ }
66
74
  return {
67
75
  success: true,
68
76
  action: { type: ACTION_TYPE_POST, ...payload },
69
77
  };
70
78
  }
79
+ function isTransactionResultPayload(payload) {
80
+ return (payload !== null &&
81
+ typeof payload === "object" &&
82
+ "type" in payload &&
83
+ payload.type === ACTION_TYPE_TRANSACTION_RESULT);
84
+ }
71
85
  /**
72
86
  * Shared pipeline for authenticated snap requests: parse the JFS envelope,
73
87
  * decode and schema-validate the payload, optionally verify the JFS signature
@@ -88,7 +102,9 @@ async function validateJfsPayload({ jfsText, schema, request, options, invalidJs
88
102
  };
89
103
  }
90
104
  const jfs = parsed.jfs;
91
- const payloadParsed = schema.safeParse(decodePayload(jfs.payload));
105
+ const decodedPayload = decodePayload(jfs.payload);
106
+ const selectedSchema = typeof schema === "function" ? schema(decodedPayload) : schema;
107
+ const payloadParsed = selectedSchema.safeParse(decodedPayload);
92
108
  if (!payloadParsed.success) {
93
109
  return {
94
110
  ok: false,
@@ -42,5 +42,6 @@ export declare const buttonProps: z.ZodObject<{
42
42
  "trending-up": "trending-up";
43
43
  "trending-down": "trending-down";
44
44
  }>>;
45
+ disabled: z.ZodOptional<z.ZodBoolean>;
45
46
  }, z.core.$strip>;
46
47
  export type ButtonProps = z.infer<typeof buttonProps>;
package/dist/ui/button.js CHANGED
@@ -6,4 +6,5 @@ export const buttonProps = z.object({
6
6
  label: z.string().min(1).max(BUTTON_MAX_LABEL_CHARS),
7
7
  variant: z.enum(BUTTON_VARIANTS).optional(),
8
8
  icon: z.enum(ICON_NAMES).optional(),
9
+ disabled: z.boolean().optional(),
9
10
  });
@@ -136,6 +136,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
136
136
  "trending-up": "trending-up";
137
137
  "trending-down": "trending-down";
138
138
  }>>;
139
+ disabled: z.ZodOptional<z.ZodBoolean>;
139
140
  }, z.core.$strip>;
140
141
  description: string;
141
142
  };
@@ -509,48 +510,56 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
509
510
  submit: {
510
511
  description: string;
511
512
  params: z.ZodObject<{
513
+ activityKey: z.ZodOptional<z.ZodString>;
512
514
  target: z.ZodString;
513
515
  }, z.core.$strip>;
514
516
  };
515
517
  open_url: {
516
518
  description: string;
517
519
  params: z.ZodObject<{
520
+ activityKey: z.ZodOptional<z.ZodString>;
518
521
  target: z.ZodString;
519
522
  }, z.core.$strip>;
520
523
  };
521
524
  open_snap: {
522
525
  description: string;
523
526
  params: z.ZodObject<{
527
+ activityKey: z.ZodOptional<z.ZodString>;
524
528
  target: z.ZodString;
525
529
  }, z.core.$strip>;
526
530
  };
527
531
  open_mini_app: {
528
532
  description: string;
529
533
  params: z.ZodObject<{
534
+ activityKey: z.ZodOptional<z.ZodString>;
530
535
  target: z.ZodString;
531
536
  }, z.core.$strip>;
532
537
  };
533
538
  view_cast: {
534
539
  description: string;
535
540
  params: z.ZodObject<{
541
+ activityKey: z.ZodOptional<z.ZodString>;
536
542
  hash: z.ZodString;
537
543
  }, z.core.$strip>;
538
544
  };
539
545
  view_profile: {
540
546
  description: string;
541
547
  params: z.ZodObject<{
548
+ activityKey: z.ZodOptional<z.ZodString>;
542
549
  fid: z.ZodNumber;
543
550
  }, z.core.$strip>;
544
551
  };
545
552
  view_channel: {
546
553
  description: string;
547
554
  params: z.ZodObject<{
555
+ activityKey: z.ZodOptional<z.ZodString>;
548
556
  channelKey: z.ZodString;
549
557
  }, z.core.$strip>;
550
558
  };
551
559
  compose_cast: {
552
560
  description: string;
553
561
  params: z.ZodObject<{
562
+ activityKey: z.ZodOptional<z.ZodString>;
554
563
  text: z.ZodOptional<z.ZodString>;
555
564
  channelKey: z.ZodOptional<z.ZodString>;
556
565
  embeds: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -559,12 +568,14 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
559
568
  view_token: {
560
569
  description: string;
561
570
  params: z.ZodObject<{
571
+ activityKey: z.ZodOptional<z.ZodString>;
562
572
  token: z.ZodString;
563
573
  }, z.core.$strip>;
564
574
  };
565
575
  send_token: {
566
576
  description: string;
567
577
  params: z.ZodObject<{
578
+ activityKey: z.ZodOptional<z.ZodString>;
568
579
  token: z.ZodString;
569
580
  amount: z.ZodOptional<z.ZodString>;
570
581
  recipientFid: z.ZodOptional<z.ZodNumber>;
@@ -574,6 +585,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
574
585
  swap_token: {
575
586
  description: string;
576
587
  params: z.ZodObject<{
588
+ activityKey: z.ZodOptional<z.ZodString>;
577
589
  sellToken: z.ZodOptional<z.ZodString>;
578
590
  buyToken: z.ZodOptional<z.ZodString>;
579
591
  }, z.core.$strip>;
@@ -581,6 +593,7 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
581
593
  send_transaction: {
582
594
  description: string;
583
595
  params: z.ZodObject<{
596
+ activityKey: z.ZodOptional<z.ZodString>;
584
597
  chainId: z.ZodString;
585
598
  to: z.ZodString;
586
599
  data: z.ZodOptional<z.ZodString>;
@@ -21,6 +21,9 @@ import { cellGridProps } from "./cell-grid.js";
21
21
  const snapClientParams = z.object({
22
22
  client_action: z.record(z.string(), z.unknown()),
23
23
  });
24
+ const activityParams = {
25
+ activityKey: z.string().min(1).max(64).optional(),
26
+ };
24
27
  /**
25
28
  * json-render catalog for snap elements.
26
29
  *
@@ -101,31 +104,31 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
101
104
  actions: {
102
105
  submit: {
103
106
  description: "POST to snap server with signed body (fid, inputs, timestamp, signature); response is next snap page.",
104
- params: z.object({ target: z.string() }),
107
+ params: z.object({ target: z.string(), ...activityParams }),
105
108
  },
106
109
  open_url: {
107
110
  description: "Open external URL in browser.",
108
- params: z.object({ target: z.string() }),
111
+ params: z.object({ target: z.string(), ...activityParams }),
109
112
  },
110
113
  open_snap: {
111
114
  description: "Open a snap URL inline. The client renders the target as a snap rather than opening a browser.",
112
- params: z.object({ target: z.string() }),
115
+ params: z.object({ target: z.string(), ...activityParams }),
113
116
  },
114
117
  open_mini_app: {
115
118
  description: "Open target URL as a Farcaster mini app.",
116
- params: z.object({ target: z.string() }),
119
+ params: z.object({ target: z.string(), ...activityParams }),
117
120
  },
118
121
  view_cast: {
119
122
  description: "Navigate to a cast by hash.",
120
- params: z.object({ hash: z.string() }),
123
+ params: z.object({ hash: z.string(), ...activityParams }),
121
124
  },
122
125
  view_profile: {
123
126
  description: "Navigate to a user profile by FID.",
124
- params: z.object({ fid: z.number() }),
127
+ params: z.object({ fid: z.number(), ...activityParams }),
125
128
  },
126
129
  view_channel: {
127
130
  description: "Navigate to a Farcaster channel by channel key.",
128
- params: z.object({ channelKey: z.string() }),
131
+ params: z.object({ channelKey: z.string(), ...activityParams }),
129
132
  },
130
133
  compose_cast: {
131
134
  description: "Open the cast composer with optional pre-filled content.",
@@ -133,11 +136,12 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
133
136
  text: z.string().optional(),
134
137
  channelKey: z.string().optional(),
135
138
  embeds: z.array(z.string()).optional(),
139
+ ...activityParams,
136
140
  }),
137
141
  },
138
142
  view_token: {
139
143
  description: "View a token in the wallet. Token is a CAIP-19 identifier.",
140
- params: z.object({ token: z.string() }),
144
+ params: z.object({ token: z.string(), ...activityParams }),
141
145
  },
142
146
  send_token: {
143
147
  description: "Open send flow for a token. Token is CAIP-19.",
@@ -146,6 +150,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
146
150
  amount: z.string().optional(),
147
151
  recipientFid: z.number().optional(),
148
152
  recipientAddress: z.string().optional(),
153
+ ...activityParams,
149
154
  }),
150
155
  },
151
156
  swap_token: {
@@ -153,6 +158,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
153
158
  params: z.object({
154
159
  sellToken: z.string().optional(),
155
160
  buyToken: z.string().optional(),
161
+ ...activityParams,
156
162
  }),
157
163
  },
158
164
  send_transaction: {
@@ -166,6 +172,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
166
172
  gasPrice: z.string().optional(),
167
173
  maxFeePerGas: z.string().optional(),
168
174
  maxPriorityFeePerGas: z.string().optional(),
175
+ ...activityParams,
169
176
  }),
170
177
  },
171
178
  paginator_next: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farcaster/snap",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "Farcaster Snaps 🫰",
5
5
  "repository": {
6
6
  "type": "git",
package/src/index.ts CHANGED
@@ -32,11 +32,15 @@ export {
32
32
  export {
33
33
  ACTION_TYPE_GET,
34
34
  ACTION_TYPE_POST,
35
+ ACTION_TYPE_TRANSACTION_RESULT,
35
36
  snapResponseSchema,
36
37
  payloadSchema,
37
38
  getPayloadSchema,
39
+ transactionResultPayloadSchema,
40
+ snapTransactionResultSchema,
38
41
  type SnapAction,
39
42
  type SnapGetAction,
43
+ type SnapTransactionResultAction,
40
44
  type SnapContext,
41
45
  type SnapResponse,
42
46
  type SnapHandlerResult,
@@ -45,6 +49,9 @@ export {
45
49
  type SnapFunction,
46
50
  type SnapPayload,
47
51
  type SnapGetPayload,
52
+ type SnapSendTransactionParams,
53
+ type SnapTransactionResult,
54
+ type SnapTransactionResultPayload,
48
55
  } from "./schemas";
49
56
  export { validateSnapResponse, type ValidationResult } from "./validator";
50
57
  export type { SnapRenderState } from "./render-state";
@@ -1,6 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { createRenderer } from "@json-render/react";
3
+ import {
4
+ JSONUIProvider,
5
+ Renderer,
6
+ type ComponentRegistry,
7
+ type CreateRendererProps,
8
+ } from "@json-render/react";
9
+ import { useMemo, type ReactNode } from "react";
4
10
  import { snapJsonRenderCatalog } from "@farcaster/snap/ui";
5
11
  import { SnapActionButton } from "./components/action-button";
6
12
  import { SnapBadge } from "./components/badge";
@@ -24,7 +30,7 @@ import { SnapCellGrid } from "./components/cell-grid";
24
30
  * Maps snap json-render catalog types to React components.
25
31
  * Keys match the snap wire-format `type` strings exactly.
26
32
  */
27
- export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
33
+ const snapCatalogRegistry = {
28
34
  badge: SnapBadge,
29
35
  button: SnapActionButton,
30
36
  icon: SnapIcon,
@@ -42,4 +48,52 @@ export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
42
48
  toggle_group: SnapToggleGroup,
43
49
  bar_chart: SnapBarChart,
44
50
  cell_grid: SnapCellGrid,
45
- });
51
+ } satisfies ComponentRegistry;
52
+
53
+ export function SnapCatalogView({
54
+ spec,
55
+ store,
56
+ state,
57
+ onAction,
58
+ onStateChange,
59
+ functions,
60
+ loading,
61
+ fallback,
62
+ children,
63
+ }: CreateRendererProps & { children?: ReactNode }) {
64
+ const actionHandlers = useMemo(
65
+ () =>
66
+ onAction
67
+ ? new Proxy<Record<string, (params: Record<string, unknown>) => unknown>>(
68
+ {},
69
+ {
70
+ get: (_target, prop) => {
71
+ return (params: Record<string, unknown>) =>
72
+ onAction(String(prop), params);
73
+ },
74
+ has: () => true,
75
+ },
76
+ )
77
+ : undefined,
78
+ [onAction],
79
+ );
80
+
81
+ return (
82
+ <JSONUIProvider
83
+ registry={snapCatalogRegistry}
84
+ store={store}
85
+ initialState={state}
86
+ handlers={actionHandlers}
87
+ functions={functions}
88
+ onStateChange={onStateChange}
89
+ >
90
+ <Renderer
91
+ spec={spec}
92
+ registry={snapCatalogRegistry}
93
+ loading={loading}
94
+ fallback={fallback}
95
+ />
96
+ {children}
97
+ </JSONUIProvider>
98
+ );
99
+ }