@farcaster/snap 0.0.0-canary-20260330145610

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.
@@ -0,0 +1,135 @@
1
+ export declare const POST_GRID_TAP_KEY: "grid_tap";
2
+ export declare const SPEC_VERSION: "1.0";
3
+ export declare const MEDIA_TYPE: "application/json+farcaster-snap";
4
+ export declare const LIMITS: {
5
+ readonly maxElementsPerPage: 5;
6
+ readonly maxButtonsPerPage: 4;
7
+ readonly maxVideoDurationSeconds: 30;
8
+ readonly maxTextInputChars: 280;
9
+ readonly maxListItems: 4;
10
+ readonly minListItems: 1;
11
+ readonly minButtonGroupOptions: 2;
12
+ readonly maxButtonGroupOptions: 4;
13
+ readonly maxButtonGroupOptionChars: 40;
14
+ readonly maxButtonLabelChars: 30;
15
+ readonly listItemContentMaxChars: 100;
16
+ readonly listItemTrailingMaxChars: 40;
17
+ readonly minGridCols: 2;
18
+ readonly maxGridCols: 64;
19
+ readonly minGridRows: 2;
20
+ readonly maxGridRows: 8;
21
+ readonly minGroupChildren: 2;
22
+ readonly maxGroupChildren: 3;
23
+ readonly maxBarChartBars: 6;
24
+ readonly barChartLabelMaxChars: 40;
25
+ readonly maxEstimatedPageHeightPx: 500;
26
+ };
27
+ export declare const TEXT_STYLE: {
28
+ readonly title: "title";
29
+ readonly body: "body";
30
+ readonly caption: "caption";
31
+ readonly label: "label";
32
+ };
33
+ export declare const TEXT_STYLE_VALUES: readonly ["title", "body", "caption", "label"];
34
+ export declare const TEXT_ALIGN_VALUES: readonly ["left", "center", "right"];
35
+ export declare const IMAGE_ASPECT_VALUES: readonly ["1:1", "16:9", "4:3", "3:4", "9:16"];
36
+ export declare const VIDEO_ASPECT_VALUES: readonly ["1:1", "16:9", "9:16"];
37
+ export declare const SPACER_SIZE: {
38
+ readonly small: "small";
39
+ readonly medium: "medium";
40
+ readonly large: "large";
41
+ };
42
+ export declare const SPACER_SIZE_VALUES: readonly ["small", "medium", "large"];
43
+ /**
44
+ * Named color palette for snaps. Snap authors specify a name; the client maps
45
+ * it to a hex value appropriate for its current light/dark mode.
46
+ *
47
+ * Light-mode hex values (used by emulator):
48
+ * gray=#8F8F8F blue=#006BFF red=#FC0036 amber=#FFAE00
49
+ * green=#28A948 teal=#00AC96 purple=#8B5CF6 pink=#F32782
50
+ *
51
+ * Dark-mode hex values (for reference; client-owned):
52
+ * gray=#8F8F8F blue=#006FFE red=#F13342 amber=#FFAE00
53
+ * green=#00AC3A teal=#00AA96 purple=#A78BFA pink=#F12B82
54
+ */
55
+ export declare const PALETTE_COLOR: {
56
+ readonly gray: "gray";
57
+ readonly blue: "blue";
58
+ readonly red: "red";
59
+ readonly amber: "amber";
60
+ readonly green: "green";
61
+ readonly teal: "teal";
62
+ readonly purple: "purple";
63
+ readonly pink: "pink";
64
+ };
65
+ export declare const PALETTE_COLOR_VALUES: readonly ["gray", "blue", "red", "amber", "green", "teal", "purple", "pink"];
66
+ export type PaletteColor = (typeof PALETTE_COLOR_VALUES)[number];
67
+ /** Light-mode hex for each palette color (emulator / reference client). */
68
+ export declare const PALETTE_LIGHT_HEX: Record<PaletteColor, string>;
69
+ /** Dark-mode hex for each palette color (reference). */
70
+ export declare const PALETTE_DARK_HEX: Record<PaletteColor, string>;
71
+ export declare const PROGRESS_COLOR_VALUES: readonly ["accent", "gray", "blue", "red", "amber", "green", "teal", "purple", "pink"];
72
+ export declare const LIST_STYLE_VALUES: readonly ["ordered", "unordered", "plain"];
73
+ export declare const DEFAULT_LIST_STYLE: "ordered";
74
+ export declare const GRID_CELL_SIZE_VALUES: readonly ["auto", "square"];
75
+ export declare const GRID_GAP_VALUES: readonly ["none", "small", "medium"];
76
+ export declare const BUTTON_GROUP_STYLE: {
77
+ readonly row: "row";
78
+ readonly stack: "stack";
79
+ readonly grid: "grid";
80
+ };
81
+ export declare const BUTTON_GROUP_STYLE_VALUES: readonly ["row", "stack", "grid"];
82
+ export declare const BUTTON_ACTION: {
83
+ readonly post: "post";
84
+ readonly link: "link";
85
+ readonly mini_app: "mini_app";
86
+ readonly sdk: "sdk";
87
+ };
88
+ export declare const BUTTON_ACTION_VALUES: readonly ["post", "link", "mini_app", "sdk"];
89
+ export declare const BUTTON_STYLE: {
90
+ readonly primary: "primary";
91
+ readonly secondary: "secondary";
92
+ };
93
+ export declare const BUTTON_STYLE_VALUES: readonly ["primary", "secondary"];
94
+ export declare const BUTTON_LAYOUT_VALUES: readonly ["stack", "row", "grid"];
95
+ export declare const DEFAULT_BUTTON_LAYOUT: "stack";
96
+ export declare const BAR_CHART_COLOR_VALUES: readonly ["accent", "gray", "blue", "red", "amber", "green", "teal", "purple", "pink"];
97
+ export declare const EFFECT_VALUES: readonly ["confetti"];
98
+ export declare const GROUP_LAYOUT_VALUES: readonly ["row"];
99
+ /** Only valid as `page.elements`: vertical container for the page body (matches json-render trees). */
100
+ export declare const PAGE_ROOT_TYPE: {
101
+ readonly stack: "stack";
102
+ };
103
+ export declare const ELEMENT_TYPE: {
104
+ readonly text: "text";
105
+ readonly image: "image";
106
+ readonly video: "video";
107
+ readonly divider: "divider";
108
+ readonly spacer: "spacer";
109
+ readonly progress: "progress";
110
+ readonly list: "list";
111
+ readonly grid: "grid";
112
+ readonly text_input: "text_input";
113
+ readonly slider: "slider";
114
+ readonly button_group: "button_group";
115
+ readonly toggle: "toggle";
116
+ readonly group: "group";
117
+ readonly bar_chart: "bar_chart";
118
+ };
119
+ export type ElementType = (typeof ELEMENT_TYPE)[keyof typeof ELEMENT_TYPE];
120
+ export declare const HTTPS_PREFIX: "https://";
121
+ export declare const HTTP_PREFIX: "http://";
122
+ /** 6-digit hex only (#RRGGBB); used for grid cell backgrounds (free hex). */
123
+ export declare const HEX_COLOR_6_RE: RegExp;
124
+ /** Default snap accent when `page.theme` or `page.theme.accent` is omitted (SPEC.md). */
125
+ export declare const DEFAULT_THEME_ACCENT: "purple";
126
+ export declare const TEXT_CONTENT_MAX: {
127
+ readonly title: 80;
128
+ readonly body: 160;
129
+ readonly caption: 100;
130
+ readonly label: 40;
131
+ };
132
+ export declare const SLIDER_STEP_ALIGN_EPS = 0.000001;
133
+ export declare const DEFAULT_SLIDER_STEP: 1;
134
+ export declare const MEDIA_ELEMENT_TYPES: ElementType[];
135
+ export declare const INTERACTIVE_ELEMENT_TYPES: ElementType[];
@@ -0,0 +1,202 @@
1
+ export const POST_GRID_TAP_KEY = "grid_tap";
2
+ export const SPEC_VERSION = "1.0";
3
+ export const MEDIA_TYPE = "application/json+farcaster-snap";
4
+ export const LIMITS = {
5
+ maxElementsPerPage: 5,
6
+ maxButtonsPerPage: 4,
7
+ maxVideoDurationSeconds: 30,
8
+ maxTextInputChars: 280,
9
+ maxListItems: 4,
10
+ minListItems: 1,
11
+ minButtonGroupOptions: 2,
12
+ maxButtonGroupOptions: 4,
13
+ maxButtonGroupOptionChars: 40,
14
+ maxButtonLabelChars: 30,
15
+ listItemContentMaxChars: 100,
16
+ listItemTrailingMaxChars: 40,
17
+ minGridCols: 2,
18
+ maxGridCols: 64,
19
+ minGridRows: 2,
20
+ maxGridRows: 8,
21
+ minGroupChildren: 2,
22
+ maxGroupChildren: 3,
23
+ maxBarChartBars: 6,
24
+ barChartLabelMaxChars: 40,
25
+ maxEstimatedPageHeightPx: 500,
26
+ };
27
+ export const TEXT_STYLE = {
28
+ title: "title",
29
+ body: "body",
30
+ caption: "caption",
31
+ label: "label",
32
+ };
33
+ export const TEXT_STYLE_VALUES = [
34
+ TEXT_STYLE.title,
35
+ TEXT_STYLE.body,
36
+ TEXT_STYLE.caption,
37
+ TEXT_STYLE.label,
38
+ ];
39
+ export const TEXT_ALIGN_VALUES = ["left", "center", "right"];
40
+ export const IMAGE_ASPECT_VALUES = [
41
+ "1:1",
42
+ "16:9",
43
+ "4:3",
44
+ "3:4",
45
+ "9:16",
46
+ ];
47
+ export const VIDEO_ASPECT_VALUES = ["1:1", "16:9", "9:16"];
48
+ export const SPACER_SIZE = {
49
+ small: "small",
50
+ medium: "medium",
51
+ large: "large",
52
+ };
53
+ export const SPACER_SIZE_VALUES = [
54
+ SPACER_SIZE.small,
55
+ SPACER_SIZE.medium,
56
+ SPACER_SIZE.large,
57
+ ];
58
+ /**
59
+ * Named color palette for snaps. Snap authors specify a name; the client maps
60
+ * it to a hex value appropriate for its current light/dark mode.
61
+ *
62
+ * Light-mode hex values (used by emulator):
63
+ * gray=#8F8F8F blue=#006BFF red=#FC0036 amber=#FFAE00
64
+ * green=#28A948 teal=#00AC96 purple=#8B5CF6 pink=#F32782
65
+ *
66
+ * Dark-mode hex values (for reference; client-owned):
67
+ * gray=#8F8F8F blue=#006FFE red=#F13342 amber=#FFAE00
68
+ * green=#00AC3A teal=#00AA96 purple=#A78BFA pink=#F12B82
69
+ */
70
+ export const PALETTE_COLOR = {
71
+ gray: "gray",
72
+ blue: "blue",
73
+ red: "red",
74
+ amber: "amber",
75
+ green: "green",
76
+ teal: "teal",
77
+ purple: "purple",
78
+ pink: "pink",
79
+ };
80
+ export const PALETTE_COLOR_VALUES = [
81
+ PALETTE_COLOR.gray,
82
+ PALETTE_COLOR.blue,
83
+ PALETTE_COLOR.red,
84
+ PALETTE_COLOR.amber,
85
+ PALETTE_COLOR.green,
86
+ PALETTE_COLOR.teal,
87
+ PALETTE_COLOR.purple,
88
+ PALETTE_COLOR.pink,
89
+ ];
90
+ /** Light-mode hex for each palette color (emulator / reference client). */
91
+ export const PALETTE_LIGHT_HEX = {
92
+ gray: "#8F8F8F",
93
+ blue: "#006BFF",
94
+ red: "#FC0036",
95
+ amber: "#FFAE00",
96
+ green: "#28A948",
97
+ teal: "#00AC96",
98
+ purple: "#8B5CF6",
99
+ pink: "#F32782",
100
+ };
101
+ /** Dark-mode hex for each palette color (reference). */
102
+ export const PALETTE_DARK_HEX = {
103
+ gray: "#8F8F8F",
104
+ blue: "#006FFE",
105
+ red: "#F13342",
106
+ amber: "#FFAE00",
107
+ green: "#00AC3A",
108
+ teal: "#00AA96",
109
+ purple: "#A78BFA",
110
+ pink: "#F12B82",
111
+ };
112
+ export const PROGRESS_COLOR_VALUES = [
113
+ "accent",
114
+ ...PALETTE_COLOR_VALUES,
115
+ ];
116
+ export const LIST_STYLE_VALUES = ["ordered", "unordered", "plain"];
117
+ export const DEFAULT_LIST_STYLE = "ordered";
118
+ export const GRID_CELL_SIZE_VALUES = ["auto", "square"];
119
+ export const GRID_GAP_VALUES = ["none", "small", "medium"];
120
+ export const BUTTON_GROUP_STYLE = {
121
+ row: "row",
122
+ stack: "stack",
123
+ grid: "grid",
124
+ };
125
+ export const BUTTON_GROUP_STYLE_VALUES = [
126
+ BUTTON_GROUP_STYLE.row,
127
+ BUTTON_GROUP_STYLE.stack,
128
+ BUTTON_GROUP_STYLE.grid,
129
+ ];
130
+ export const BUTTON_ACTION = {
131
+ post: "post",
132
+ link: "link",
133
+ mini_app: "mini_app",
134
+ sdk: "sdk",
135
+ };
136
+ export const BUTTON_ACTION_VALUES = [
137
+ BUTTON_ACTION.post,
138
+ BUTTON_ACTION.link,
139
+ BUTTON_ACTION.mini_app,
140
+ BUTTON_ACTION.sdk,
141
+ ];
142
+ export const BUTTON_STYLE = {
143
+ primary: "primary",
144
+ secondary: "secondary",
145
+ };
146
+ export const BUTTON_STYLE_VALUES = [
147
+ BUTTON_STYLE.primary,
148
+ BUTTON_STYLE.secondary,
149
+ ];
150
+ export const BUTTON_LAYOUT_VALUES = ["stack", "row", "grid"];
151
+ export const DEFAULT_BUTTON_LAYOUT = BUTTON_LAYOUT_VALUES[0];
152
+ export const BAR_CHART_COLOR_VALUES = [
153
+ "accent",
154
+ ...PALETTE_COLOR_VALUES,
155
+ ];
156
+ export const EFFECT_VALUES = ["confetti"];
157
+ export const GROUP_LAYOUT_VALUES = ["row"];
158
+ /** Only valid as `page.elements`: vertical container for the page body (matches json-render trees). */
159
+ export const PAGE_ROOT_TYPE = {
160
+ stack: "stack",
161
+ };
162
+ export const ELEMENT_TYPE = {
163
+ text: "text",
164
+ image: "image",
165
+ video: "video",
166
+ divider: "divider",
167
+ spacer: "spacer",
168
+ progress: "progress",
169
+ list: "list",
170
+ grid: "grid",
171
+ text_input: "text_input",
172
+ slider: "slider",
173
+ button_group: "button_group",
174
+ toggle: "toggle",
175
+ group: "group",
176
+ bar_chart: "bar_chart",
177
+ };
178
+ export const HTTPS_PREFIX = "https://";
179
+ export const HTTP_PREFIX = "http://";
180
+ /** 6-digit hex only (#RRGGBB); used for grid cell backgrounds (free hex). */
181
+ export const HEX_COLOR_6_RE = /^#[0-9a-fA-F]{6}$/;
182
+ /** Default snap accent when `page.theme` or `page.theme.accent` is omitted (SPEC.md). */
183
+ export const DEFAULT_THEME_ACCENT = PALETTE_COLOR.purple;
184
+ export const TEXT_CONTENT_MAX = {
185
+ [TEXT_STYLE.title]: 80,
186
+ [TEXT_STYLE.body]: 160,
187
+ [TEXT_STYLE.caption]: 100,
188
+ [TEXT_STYLE.label]: 40,
189
+ };
190
+ export const SLIDER_STEP_ALIGN_EPS = 1e-6;
191
+ export const DEFAULT_SLIDER_STEP = 1;
192
+ export const MEDIA_ELEMENT_TYPES = [
193
+ ELEMENT_TYPE.image,
194
+ ELEMENT_TYPE.video,
195
+ ELEMENT_TYPE.grid,
196
+ ];
197
+ export const INTERACTIVE_ELEMENT_TYPES = [
198
+ ELEMENT_TYPE.button_group,
199
+ ELEMENT_TYPE.slider,
200
+ ELEMENT_TYPE.text_input,
201
+ ELEMENT_TYPE.toggle,
202
+ ];
package/dist/http.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { type SnapAction, type SnapResponse, type SnapPage } from "./schemas.js";
2
+ import { z } from "zod";
3
+ export type ParseRequestError = {
4
+ type: "method_not_allowed";
5
+ message: string;
6
+ } | {
7
+ type: "invalid_json";
8
+ message: string;
9
+ } | {
10
+ type: "validation";
11
+ issues: z.core.$ZodIssue[];
12
+ } | {
13
+ type: "replay";
14
+ message: string;
15
+ } | {
16
+ type: "signature";
17
+ message: string;
18
+ };
19
+ export type ParseRequestOptions = {
20
+ /**
21
+ * When true, skip {@link verifyJFSRequestBody} (signature checks).
22
+ */
23
+ skipJFSVerification?: boolean;
24
+ };
25
+ export type ParseRequestResult = {
26
+ success: true;
27
+ action: SnapAction;
28
+ } | {
29
+ success: false;
30
+ error: ParseRequestError;
31
+ };
32
+ /**
33
+ * Parse and validate Farcaster snap requests:
34
+ * - `GET` is allowed for first-page loads and returns `{ type: "get" }`.
35
+ * - `POST`: the body must be JSON in JFS form (`header` / `payload` / `signature`) even if JFS verification is skipped.
36
+ */
37
+ export declare function parseRequest(request: Request, options?: ParseRequestOptions): Promise<ParseRequestResult>;
38
+ export type SendResponseOptions = ResponseInit & {
39
+ /** When false, invalid pages return 500 instead of 400. Default true. */
40
+ clientErrorOnInvalid?: boolean;
41
+ };
42
+ /**
43
+ * Validate a snap root or bare `page` object, then return a JSON Response for the client.
44
+ * Sets `Content-Type: application/json+farcaster-snap`.
45
+ *
46
+ * On validation failure returns JSON `{ "error": "...", "issues": [...] }` with status 400 or 500.
47
+ */
48
+ export declare function sendResponse(payload: SnapResponse | SnapPage, init?: SendResponseOptions): Response;
package/dist/http.js ADDED
@@ -0,0 +1,146 @@
1
+ import { MEDIA_TYPE, SPEC_VERSION } from "./constants.js";
2
+ import { decodePayload, verifyJFSRequestBody } from "./verify.js";
3
+ import { payloadSchema, rootSchema, } from "./schemas.js";
4
+ import { validatePage } from "./validator.js";
5
+ import { z } from "zod";
6
+ /** Default replay window per SPEC.md § Replay Protection (5 minutes). */
7
+ const DEFAULT_SNAP_POST_MAX_SKEW_SECONDS = 300;
8
+ function isRecord(v) {
9
+ return v !== null && typeof v === "object" && !Array.isArray(v);
10
+ }
11
+ function normalizeToRoot(payload) {
12
+ if (isRecord(payload) && "version" in payload && "page" in payload) {
13
+ return payload;
14
+ }
15
+ return { version: SPEC_VERSION, page: payload };
16
+ }
17
+ const requestBodySchema = z.object({
18
+ header: z.string(),
19
+ payload: z.string(),
20
+ signature: z.string(),
21
+ });
22
+ /**
23
+ * Parse and validate Farcaster snap requests:
24
+ * - `GET` is allowed for first-page loads and returns `{ type: "get" }`.
25
+ * - `POST`: the body must be JSON in JFS form (`header` / `payload` / `signature`) even if JFS verification is skipped.
26
+ */
27
+ export async function parseRequest(request, options = {}) {
28
+ if (!["GET", "POST"].includes(request.method)) {
29
+ return {
30
+ success: false,
31
+ error: {
32
+ type: "method_not_allowed",
33
+ message: `expected POST, received ${request.method}`,
34
+ },
35
+ };
36
+ }
37
+ if (request.method === "GET") {
38
+ return {
39
+ success: true,
40
+ action: { type: "get" },
41
+ };
42
+ }
43
+ const maxSkew = DEFAULT_SNAP_POST_MAX_SKEW_SECONDS;
44
+ const nowSec = Math.floor(Date.now() / 1000);
45
+ const text = await request.text();
46
+ let jsonBody;
47
+ try {
48
+ jsonBody = JSON.parse(text);
49
+ }
50
+ catch {
51
+ return {
52
+ success: false,
53
+ error: {
54
+ type: "invalid_json",
55
+ message: "request body is not valid JSON",
56
+ },
57
+ };
58
+ }
59
+ const parsed = requestBodySchema.safeParse(jsonBody);
60
+ if (!parsed.success) {
61
+ return {
62
+ success: false,
63
+ error: { type: "invalid_json", message: parsed.error.message },
64
+ };
65
+ }
66
+ if (!options.skipJFSVerification) {
67
+ const jfs = await verifyJFSRequestBody(parsed.data);
68
+ if (!jfs.valid) {
69
+ return {
70
+ success: false,
71
+ error: { type: "signature", message: jfs.error.message },
72
+ };
73
+ }
74
+ }
75
+ const payloadParsed = payloadSchema.safeParse(decodePayload(parsed.data.payload));
76
+ if (!payloadParsed.success) {
77
+ return {
78
+ success: false,
79
+ error: { type: "validation", issues: payloadParsed.error.issues },
80
+ };
81
+ }
82
+ const body = payloadParsed.data;
83
+ if (Math.abs(nowSec - body.timestamp) > maxSkew) {
84
+ return {
85
+ success: false,
86
+ error: {
87
+ type: "replay",
88
+ message: `timestamp outside allowed skew of ${maxSkew}s`,
89
+ },
90
+ };
91
+ }
92
+ return {
93
+ success: true,
94
+ action: {
95
+ type: "post",
96
+ fid: body.fid,
97
+ inputs: body.inputs,
98
+ buttonIndex: body.button_index,
99
+ timestamp: body.timestamp,
100
+ },
101
+ };
102
+ }
103
+ /**
104
+ * Validate a snap root or bare `page` object, then return a JSON Response for the client.
105
+ * Sets `Content-Type: application/json+farcaster-snap`.
106
+ *
107
+ * On validation failure returns JSON `{ "error": "...", "issues": [...] }` with status 400 or 500.
108
+ */
109
+ export function sendResponse(payload, init) {
110
+ const rootUnknown = normalizeToRoot(payload);
111
+ const validation = validatePage(rootUnknown);
112
+ const clientError = init?.clientErrorOnInvalid !== false;
113
+ const status = init?.status;
114
+ if (!validation.valid) {
115
+ const errStatus = clientError ? 400 : 500;
116
+ return new Response(JSON.stringify({
117
+ error: clientError
118
+ ? "invalid snap page"
119
+ : "server produced an invalid snap page",
120
+ issues: validation.issues,
121
+ }), {
122
+ status: status ?? errStatus,
123
+ headers: {
124
+ "Content-Type": "application/json; charset=utf-8",
125
+ ...normalizeHeaders(init?.headers),
126
+ },
127
+ });
128
+ }
129
+ const finalized = rootSchema.parse(rootUnknown);
130
+ const headers = new Headers(init?.headers);
131
+ headers.set("Content-Type", `${MEDIA_TYPE}; charset=utf-8`);
132
+ return new Response(JSON.stringify(finalized), {
133
+ ...init,
134
+ status: status ?? init?.status ?? 200,
135
+ headers,
136
+ });
137
+ }
138
+ function normalizeHeaders(h) {
139
+ if (h === undefined)
140
+ return {};
141
+ const out = {};
142
+ new Headers(h).forEach((value, key) => {
143
+ out[key] = value;
144
+ });
145
+ return out;
146
+ }
package/dist/hubs.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type Hex } from "viem";
2
+ /**
3
+ * Default hub HTTP base URL (Hubble HTTP API on the public hub TLS port).
4
+ */
5
+ export declare const DEFAULT_SNAP_HUB_HTTP_BASE_URL = "https://rho.farcaster.xyz:3381";
6
+ type DecodedSignedKeyRequestMetadata = {
7
+ requestFid: bigint;
8
+ requestSigner: Hex;
9
+ signature: Uint8Array;
10
+ deadline: bigint;
11
+ };
12
+ type ActiveEd25519Signer = {
13
+ publicKey: Uint8Array;
14
+ /**
15
+ * From the latest ADD event for this key. `null` when metadata is empty or not decodable
16
+ * as `SignedKeyRequestMetadata`.
17
+ */
18
+ signedKeyRequestMetadata: DecodedSignedKeyRequestMetadata | null;
19
+ };
20
+ type FetchSignerKeysResult = {
21
+ ok: true;
22
+ signers: ActiveEd25519Signer[];
23
+ } | {
24
+ ok: false;
25
+ message: string;
26
+ };
27
+ type FetchSignerKeysFromHubHttpOptions = {
28
+ /** Override global `fetch` (tests or non-Request environments). */
29
+ fetchFn?: typeof fetch;
30
+ signal?: AbortSignal;
31
+ };
32
+ /**
33
+ * Loads on-chain signer events via Hubble HTTP API (`GET /v1/onChainSignersByFid`)
34
+ * and returns active Ed25519 delegate keys from signer events.
35
+ */
36
+ export declare function getActiveEd25519SignerKeysFromHubHttp(httpBaseUrl: string, fid: number, options?: FetchSignerKeysFromHubHttpOptions): Promise<FetchSignerKeysResult>;
37
+ export {};