@beeperbot/sdk 0.1.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.
- package/dist/index.cjs +3355 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4480 -0
- package/dist/index.d.ts +4480 -0
- package/dist/index.js +3202 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3355 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
var crypto = require('crypto');
|
|
7
|
+
|
|
8
|
+
// src/client/http.ts
|
|
9
|
+
|
|
10
|
+
// src/errors/codes.ts
|
|
11
|
+
var ErrorCodes = {
|
|
12
|
+
// Quote-related errors
|
|
13
|
+
QUOTE_EXPIRED: "QUOTE_EXPIRED",
|
|
14
|
+
QUOTE_NOT_FOUND: "QUOTE_NOT_FOUND",
|
|
15
|
+
QUOTE_INVALID: "QUOTE_INVALID",
|
|
16
|
+
// Recipient-related errors
|
|
17
|
+
INVALID_RECIPIENT: "INVALID_RECIPIENT",
|
|
18
|
+
RECIPIENT_NOT_FOUND: "RECIPIENT_NOT_FOUND",
|
|
19
|
+
// Filter-related errors
|
|
20
|
+
INVALID_FILTER: "INVALID_FILTER",
|
|
21
|
+
FILTER_SYNTAX_ERROR: "FILTER_SYNTAX_ERROR",
|
|
22
|
+
// Budget-related errors
|
|
23
|
+
BUDGET_TOO_LOW: "BUDGET_TOO_LOW",
|
|
24
|
+
BUDGET_EXCEEDED: "BUDGET_EXCEEDED",
|
|
25
|
+
// Payment-related errors
|
|
26
|
+
PAYMENT_NOT_FOUND: "PAYMENT_NOT_FOUND",
|
|
27
|
+
PAYMENT_INSUFFICIENT: "PAYMENT_INSUFFICIENT",
|
|
28
|
+
PAYMENT_FAILED: "PAYMENT_FAILED",
|
|
29
|
+
// Execution-related errors
|
|
30
|
+
EXECUTION_ALREADY_TRIGGERED: "EXECUTION_ALREADY_TRIGGERED",
|
|
31
|
+
EXECUTION_FAILED: "EXECUTION_FAILED",
|
|
32
|
+
EXECUTION_PENDING: "EXECUTION_PENDING",
|
|
33
|
+
// Rate limiting
|
|
34
|
+
RATE_LIMITED: "RATE_LIMITED",
|
|
35
|
+
// Authentication/Authorization
|
|
36
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
37
|
+
FORBIDDEN: "FORBIDDEN",
|
|
38
|
+
API_KEY_INVALID: "API_KEY_INVALID",
|
|
39
|
+
// Network errors
|
|
40
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
41
|
+
TIMEOUT: "TIMEOUT",
|
|
42
|
+
CONNECTION_REFUSED: "CONNECTION_REFUSED",
|
|
43
|
+
// Validation errors
|
|
44
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
45
|
+
SCHEMA_VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
|
|
46
|
+
// Server errors
|
|
47
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
48
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
|
|
49
|
+
// Draft errors
|
|
50
|
+
DRAFT_NOT_FOUND: "DRAFT_NOT_FOUND",
|
|
51
|
+
DRAFT_INVALID: "DRAFT_INVALID",
|
|
52
|
+
// Generic
|
|
53
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
54
|
+
};
|
|
55
|
+
var RETRYABLE_ERROR_CODES = [
|
|
56
|
+
ErrorCodes.RATE_LIMITED,
|
|
57
|
+
ErrorCodes.NETWORK_ERROR,
|
|
58
|
+
ErrorCodes.TIMEOUT,
|
|
59
|
+
ErrorCodes.CONNECTION_REFUSED,
|
|
60
|
+
ErrorCodes.SERVICE_UNAVAILABLE,
|
|
61
|
+
ErrorCodes.INTERNAL_ERROR
|
|
62
|
+
];
|
|
63
|
+
function isRetryableCode(code) {
|
|
64
|
+
return RETRYABLE_ERROR_CODES.includes(code);
|
|
65
|
+
}
|
|
66
|
+
var HTTP_STATUS_TO_ERROR_CODE = {
|
|
67
|
+
400: ErrorCodes.VALIDATION_ERROR,
|
|
68
|
+
401: ErrorCodes.UNAUTHORIZED,
|
|
69
|
+
403: ErrorCodes.FORBIDDEN,
|
|
70
|
+
404: ErrorCodes.UNKNOWN_ERROR,
|
|
71
|
+
// Specific 404s are mapped differently
|
|
72
|
+
408: ErrorCodes.TIMEOUT,
|
|
73
|
+
429: ErrorCodes.RATE_LIMITED,
|
|
74
|
+
500: ErrorCodes.INTERNAL_ERROR,
|
|
75
|
+
502: ErrorCodes.SERVICE_UNAVAILABLE,
|
|
76
|
+
503: ErrorCodes.SERVICE_UNAVAILABLE,
|
|
77
|
+
504: ErrorCodes.TIMEOUT
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/errors/BeeperError.ts
|
|
81
|
+
var BeeperError = class _BeeperError extends Error {
|
|
82
|
+
/** Error code identifying the type of error */
|
|
83
|
+
code;
|
|
84
|
+
/** Whether this error can be retried */
|
|
85
|
+
retryable;
|
|
86
|
+
/** Additional context about the error */
|
|
87
|
+
context;
|
|
88
|
+
/** The original error that caused this error */
|
|
89
|
+
cause;
|
|
90
|
+
constructor(options) {
|
|
91
|
+
super(options.message);
|
|
92
|
+
this.name = "BeeperError";
|
|
93
|
+
this.code = options.code;
|
|
94
|
+
this.cause = options.cause;
|
|
95
|
+
this.context = options.context ?? {};
|
|
96
|
+
this.retryable = options.retryable ?? isRetryableCode(options.code);
|
|
97
|
+
if (Error.captureStackTrace) {
|
|
98
|
+
Error.captureStackTrace(this, _BeeperError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a string representation of the error
|
|
103
|
+
*/
|
|
104
|
+
toString() {
|
|
105
|
+
let str = `BeeperError [${this.code}]: ${this.message}`;
|
|
106
|
+
if (this.context.requestId) {
|
|
107
|
+
str += ` (Request ID: ${this.context.requestId})`;
|
|
108
|
+
}
|
|
109
|
+
return str;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Convert to a plain object for logging/serialization
|
|
113
|
+
*/
|
|
114
|
+
toJSON() {
|
|
115
|
+
return {
|
|
116
|
+
name: this.name,
|
|
117
|
+
code: this.code,
|
|
118
|
+
message: this.message,
|
|
119
|
+
retryable: this.retryable,
|
|
120
|
+
context: this.context,
|
|
121
|
+
stack: this.stack
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a network error
|
|
126
|
+
*/
|
|
127
|
+
static networkError(message, cause) {
|
|
128
|
+
const options = {
|
|
129
|
+
code: ErrorCodes.NETWORK_ERROR,
|
|
130
|
+
message,
|
|
131
|
+
retryable: true
|
|
132
|
+
};
|
|
133
|
+
if (cause !== void 0) {
|
|
134
|
+
options.cause = cause;
|
|
135
|
+
}
|
|
136
|
+
return new _BeeperError(options);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Create a timeout error
|
|
140
|
+
*/
|
|
141
|
+
static timeout(endpoint, timeoutMs) {
|
|
142
|
+
return new _BeeperError({
|
|
143
|
+
code: ErrorCodes.TIMEOUT,
|
|
144
|
+
message: `Request to ${endpoint} timed out after ${timeoutMs}ms`,
|
|
145
|
+
context: { endpoint },
|
|
146
|
+
retryable: true
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create an unauthorized error
|
|
151
|
+
*/
|
|
152
|
+
static unauthorized(message = "Invalid or missing API key") {
|
|
153
|
+
return new _BeeperError({
|
|
154
|
+
code: ErrorCodes.UNAUTHORIZED,
|
|
155
|
+
message,
|
|
156
|
+
retryable: false
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a rate limited error
|
|
161
|
+
*/
|
|
162
|
+
static rateLimited(retryAfter) {
|
|
163
|
+
const context = {};
|
|
164
|
+
if (retryAfter !== void 0) {
|
|
165
|
+
context.retryAfter = retryAfter;
|
|
166
|
+
}
|
|
167
|
+
return new _BeeperError({
|
|
168
|
+
code: ErrorCodes.RATE_LIMITED,
|
|
169
|
+
message: retryAfter ? `Rate limited. Retry after ${retryAfter} seconds` : "Rate limited. Please try again later",
|
|
170
|
+
context,
|
|
171
|
+
retryable: true
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Create a validation error
|
|
176
|
+
*/
|
|
177
|
+
static validation(message, details) {
|
|
178
|
+
const context = {};
|
|
179
|
+
if (details !== void 0) {
|
|
180
|
+
context.details = details;
|
|
181
|
+
}
|
|
182
|
+
return new _BeeperError({
|
|
183
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
184
|
+
message,
|
|
185
|
+
context,
|
|
186
|
+
retryable: false
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Create a quote expired error
|
|
191
|
+
*/
|
|
192
|
+
static quoteExpired(quoteId) {
|
|
193
|
+
return new _BeeperError({
|
|
194
|
+
code: ErrorCodes.QUOTE_EXPIRED,
|
|
195
|
+
message: `Quote ${quoteId} has expired`,
|
|
196
|
+
context: { details: { quoteId } },
|
|
197
|
+
retryable: false
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create from an HTTP response
|
|
202
|
+
*/
|
|
203
|
+
static fromHttpResponse(statusCode, body, requestId) {
|
|
204
|
+
const rawBody = body;
|
|
205
|
+
const errorData = rawBody?.error ?? rawBody;
|
|
206
|
+
const code = errorData?.code ?? ErrorCodes.UNKNOWN_ERROR;
|
|
207
|
+
const message = errorData?.message ?? `HTTP ${statusCode} error`;
|
|
208
|
+
const context = { statusCode };
|
|
209
|
+
if (requestId !== void 0) {
|
|
210
|
+
context.requestId = requestId;
|
|
211
|
+
}
|
|
212
|
+
if (errorData?.details !== void 0) {
|
|
213
|
+
context.details = errorData.details;
|
|
214
|
+
}
|
|
215
|
+
return new _BeeperError({
|
|
216
|
+
code,
|
|
217
|
+
message,
|
|
218
|
+
context
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/constants.ts
|
|
224
|
+
var API_BASE_URLS = {
|
|
225
|
+
production: "https://beep.works/api/v1/sdk",
|
|
226
|
+
staging: "https://staging.beep.works/api/v1/sdk",
|
|
227
|
+
development: "http://localhost:3000/api/v1/sdk"
|
|
228
|
+
};
|
|
229
|
+
var TIMEOUTS = {
|
|
230
|
+
/** Default request timeout */
|
|
231
|
+
DEFAULT: 3e4,
|
|
232
|
+
/** Timeout for quote requests */
|
|
233
|
+
QUOTE: 1e4,
|
|
234
|
+
/** Timeout for execute requests (longer due to blockchain) */
|
|
235
|
+
EXECUTE: 6e4,
|
|
236
|
+
/** Timeout for health checks */
|
|
237
|
+
HEALTH: 5e3
|
|
238
|
+
};
|
|
239
|
+
var ENDPOINTS = {
|
|
240
|
+
HEALTH: "/send/health",
|
|
241
|
+
QUOTES: "/send/quotes",
|
|
242
|
+
KEYS: "/keys"
|
|
243
|
+
};
|
|
244
|
+
var HEADERS = {
|
|
245
|
+
IDEMPOTENCY_KEY: "Idempotency-Key",
|
|
246
|
+
AUTHORIZATION: "Authorization",
|
|
247
|
+
REQUEST_ID: "X-Request-Id",
|
|
248
|
+
CONTENT_TYPE: "Content-Type"
|
|
249
|
+
};
|
|
250
|
+
var RETRY_CONFIG = {
|
|
251
|
+
/** Maximum number of retry attempts */
|
|
252
|
+
MAX_RETRIES: 3,
|
|
253
|
+
/** Initial delay before first retry (1 second) */
|
|
254
|
+
INITIAL_DELAY_MS: 1e3,
|
|
255
|
+
/** Maximum delay between retries (30 seconds) */
|
|
256
|
+
MAX_DELAY_MS: 3e4,
|
|
257
|
+
/** Multiplier for exponential backoff */
|
|
258
|
+
BACKOFF_MULTIPLIER: 2,
|
|
259
|
+
/** Jitter factor to add randomness (0-1) */
|
|
260
|
+
JITTER_FACTOR: 0.1
|
|
261
|
+
};
|
|
262
|
+
var QUOTE_EXPIRATION_SECONDS = 300;
|
|
263
|
+
var SDK_VERSION = "0.1.0";
|
|
264
|
+
|
|
265
|
+
// src/client/http.ts
|
|
266
|
+
function createHttpConfig(options) {
|
|
267
|
+
const baseUrl = options.baseUrl ?? API_BASE_URLS[options.environment ?? "production"];
|
|
268
|
+
return {
|
|
269
|
+
apiKey: options.apiKey,
|
|
270
|
+
baseUrl,
|
|
271
|
+
timeout: options.timeout ?? TIMEOUTS.DEFAULT,
|
|
272
|
+
fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
|
|
273
|
+
debug: options.debug ?? false
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
var HttpClient = class {
|
|
277
|
+
config;
|
|
278
|
+
constructor(config) {
|
|
279
|
+
this.config = config;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Check if an HTTP status code is retryable
|
|
283
|
+
* - 5xx server errors
|
|
284
|
+
* - 429 rate limit errors
|
|
285
|
+
* Does NOT retry 4xx errors (except 429)
|
|
286
|
+
*/
|
|
287
|
+
isRetryableStatus(status) {
|
|
288
|
+
if (status === 429) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
if (status >= 500 && status < 600) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Check if an error is retryable (network errors, timeouts)
|
|
298
|
+
*/
|
|
299
|
+
isRetryableError(error) {
|
|
300
|
+
if (error.name === "AbortError") {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
if (error instanceof TypeError) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
if (error.message.includes("fetch failed") || error.message.includes("network") || error.message.includes("ECONNREFUSED") || error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT")) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Calculate delay with exponential backoff and jitter
|
|
313
|
+
*/
|
|
314
|
+
calculateDelay(attempt, retryConfig) {
|
|
315
|
+
const exponentialDelay = retryConfig.initialDelayMs * Math.pow(retryConfig.backoffMultiplier, attempt);
|
|
316
|
+
const cappedDelay = Math.min(exponentialDelay, retryConfig.maxDelayMs);
|
|
317
|
+
const jitter = cappedDelay * retryConfig.jitterFactor * Math.random();
|
|
318
|
+
return Math.floor(cappedDelay + jitter);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Sleep for a given number of milliseconds
|
|
322
|
+
*/
|
|
323
|
+
sleep(ms) {
|
|
324
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get default retry configuration
|
|
328
|
+
*/
|
|
329
|
+
getRetryConfig(options) {
|
|
330
|
+
if (options === false) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
maxRetries: options?.maxRetries ?? RETRY_CONFIG.MAX_RETRIES,
|
|
335
|
+
initialDelayMs: options?.initialDelayMs ?? RETRY_CONFIG.INITIAL_DELAY_MS,
|
|
336
|
+
maxDelayMs: options?.maxDelayMs ?? RETRY_CONFIG.MAX_DELAY_MS,
|
|
337
|
+
backoffMultiplier: options?.backoffMultiplier ?? RETRY_CONFIG.BACKOFF_MULTIPLIER,
|
|
338
|
+
jitterFactor: options?.jitterFactor ?? RETRY_CONFIG.JITTER_FACTOR
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Make an HTTP request with validation and automatic retry
|
|
343
|
+
*/
|
|
344
|
+
async request(options, responseSchema) {
|
|
345
|
+
let url = `${this.config.baseUrl}${options.endpoint}`;
|
|
346
|
+
if (options.params) {
|
|
347
|
+
const searchParams = new URLSearchParams();
|
|
348
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
349
|
+
if (value !== void 0) {
|
|
350
|
+
searchParams.append(key, String(value));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const queryString = searchParams.toString();
|
|
354
|
+
if (queryString) {
|
|
355
|
+
url += `?${queryString}`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const timeout = options.timeout ?? this.config.timeout;
|
|
359
|
+
const retryConfig = this.getRetryConfig(options.retry);
|
|
360
|
+
const startTime = Date.now();
|
|
361
|
+
let attempt = 0;
|
|
362
|
+
const maxAttempts = retryConfig ? retryConfig.maxRetries + 1 : 1;
|
|
363
|
+
while (attempt < maxAttempts) {
|
|
364
|
+
const controller = new AbortController();
|
|
365
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
366
|
+
const signal = options.signal ? this.mergeSignals(options.signal, controller.signal) : controller.signal;
|
|
367
|
+
try {
|
|
368
|
+
const headers = this.buildHeaders(options);
|
|
369
|
+
const body = options.body ? JSON.stringify(options.body) : null;
|
|
370
|
+
if (this.config.debug) {
|
|
371
|
+
console.log(`[Beeper SDK] Request:`, {
|
|
372
|
+
method: options.method,
|
|
373
|
+
url,
|
|
374
|
+
headers: this.sanitizeHeaders(headers),
|
|
375
|
+
body: options.body,
|
|
376
|
+
attempt: attempt + 1,
|
|
377
|
+
maxAttempts
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const requestInit = {
|
|
381
|
+
method: options.method,
|
|
382
|
+
headers,
|
|
383
|
+
signal
|
|
384
|
+
};
|
|
385
|
+
if (body !== null) {
|
|
386
|
+
requestInit.body = body;
|
|
387
|
+
}
|
|
388
|
+
const response = await this.config.fetch(url, requestInit);
|
|
389
|
+
clearTimeout(timeoutId);
|
|
390
|
+
const requestId = response.headers.get(HEADERS.REQUEST_ID) ?? void 0;
|
|
391
|
+
if (this.config.debug) {
|
|
392
|
+
console.log(`[Beeper SDK] Response:`, {
|
|
393
|
+
url,
|
|
394
|
+
status: response.status,
|
|
395
|
+
statusText: response.statusText,
|
|
396
|
+
requestId,
|
|
397
|
+
attempt: attempt + 1,
|
|
398
|
+
timeMs: Date.now() - startTime
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
if (!response.ok) {
|
|
402
|
+
const errorBody = await this.safeParseJson(response);
|
|
403
|
+
if (retryConfig && this.isRetryableStatus(response.status) && attempt < retryConfig.maxRetries) {
|
|
404
|
+
const delay = this.calculateDelay(attempt, retryConfig);
|
|
405
|
+
if (this.config.debug) {
|
|
406
|
+
console.log(`[Beeper SDK] Retrying:`, {
|
|
407
|
+
url,
|
|
408
|
+
status: response.status,
|
|
409
|
+
attempt: attempt + 1,
|
|
410
|
+
maxRetries: retryConfig.maxRetries,
|
|
411
|
+
delayMs: delay
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
await this.sleep(delay);
|
|
415
|
+
attempt++;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
throw BeeperError.fromHttpResponse(
|
|
419
|
+
response.status,
|
|
420
|
+
errorBody,
|
|
421
|
+
requestId
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
const data = await this.safeParseJson(response);
|
|
425
|
+
if (responseSchema) {
|
|
426
|
+
const result = responseSchema.safeParse(data);
|
|
427
|
+
if (!result.success) {
|
|
428
|
+
throw BeeperError.validation("Invalid response from server", {
|
|
429
|
+
zodErrors: result.error.errors
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
data: result.data,
|
|
434
|
+
status: response.status,
|
|
435
|
+
headers: response.headers,
|
|
436
|
+
requestId,
|
|
437
|
+
retryCount: attempt,
|
|
438
|
+
totalTimeMs: Date.now() - startTime
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
data,
|
|
443
|
+
status: response.status,
|
|
444
|
+
headers: response.headers,
|
|
445
|
+
requestId,
|
|
446
|
+
retryCount: attempt,
|
|
447
|
+
totalTimeMs: Date.now() - startTime
|
|
448
|
+
};
|
|
449
|
+
} catch (error) {
|
|
450
|
+
clearTimeout(timeoutId);
|
|
451
|
+
const isErrorInstance = error instanceof Error;
|
|
452
|
+
const shouldRetry = retryConfig && isErrorInstance && this.isRetryableError(error) && attempt < retryConfig.maxRetries;
|
|
453
|
+
if (shouldRetry) {
|
|
454
|
+
const delay = this.calculateDelay(attempt, retryConfig);
|
|
455
|
+
if (this.config.debug) {
|
|
456
|
+
console.log(`[Beeper SDK] Retrying after error:`, {
|
|
457
|
+
url,
|
|
458
|
+
error: isErrorInstance ? error.message : "Unknown error",
|
|
459
|
+
attempt: attempt + 1,
|
|
460
|
+
maxRetries: retryConfig.maxRetries,
|
|
461
|
+
delayMs: delay
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
await this.sleep(delay);
|
|
465
|
+
attempt++;
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (isErrorInstance && error.name === "AbortError") {
|
|
469
|
+
throw BeeperError.timeout(options.endpoint, timeout);
|
|
470
|
+
}
|
|
471
|
+
if (error instanceof TypeError) {
|
|
472
|
+
throw BeeperError.networkError(
|
|
473
|
+
`Network error: ${error.message}`,
|
|
474
|
+
error
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
if (error instanceof BeeperError) {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
const errorOptions = {
|
|
481
|
+
code: ErrorCodes.UNKNOWN_ERROR,
|
|
482
|
+
message: isErrorInstance ? error.message : "Unknown error"
|
|
483
|
+
};
|
|
484
|
+
if (isErrorInstance) {
|
|
485
|
+
errorOptions.cause = error;
|
|
486
|
+
}
|
|
487
|
+
throw new BeeperError(errorOptions);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
throw BeeperError.networkError(
|
|
491
|
+
`Max retries (${retryConfig?.maxRetries ?? 0}) exceeded for ${options.endpoint}`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* GET request helper
|
|
496
|
+
*/
|
|
497
|
+
async get(endpoint, options, schema) {
|
|
498
|
+
return this.request({ ...options, method: "GET", endpoint }, schema);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* POST request helper
|
|
502
|
+
*/
|
|
503
|
+
async post(endpoint, body, options, schema) {
|
|
504
|
+
return this.request(
|
|
505
|
+
{ ...options, method: "POST", endpoint, body },
|
|
506
|
+
schema
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* PUT request helper
|
|
511
|
+
*/
|
|
512
|
+
async put(endpoint, body, options, schema) {
|
|
513
|
+
return this.request(
|
|
514
|
+
{ ...options, method: "PUT", endpoint, body },
|
|
515
|
+
schema
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* PATCH request helper
|
|
520
|
+
*/
|
|
521
|
+
async patch(endpoint, body, options, schema) {
|
|
522
|
+
return this.request(
|
|
523
|
+
{ ...options, method: "PATCH", endpoint, body },
|
|
524
|
+
schema
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* DELETE request helper
|
|
529
|
+
*/
|
|
530
|
+
async delete(endpoint, options, schema) {
|
|
531
|
+
return this.request({ ...options, method: "DELETE", endpoint }, schema);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Build request headers
|
|
535
|
+
*/
|
|
536
|
+
buildHeaders(options) {
|
|
537
|
+
const headers = {
|
|
538
|
+
[HEADERS.CONTENT_TYPE]: "application/json",
|
|
539
|
+
[HEADERS.AUTHORIZATION]: `Bearer ${this.config.apiKey}`,
|
|
540
|
+
"User-Agent": `beeper-sdk/${SDK_VERSION}`,
|
|
541
|
+
...options.headers
|
|
542
|
+
};
|
|
543
|
+
if (options.idempotencyKey) {
|
|
544
|
+
headers[HEADERS.IDEMPOTENCY_KEY] = options.idempotencyKey;
|
|
545
|
+
}
|
|
546
|
+
return headers;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Sanitize headers for logging (redact sensitive values)
|
|
550
|
+
*/
|
|
551
|
+
sanitizeHeaders(headers) {
|
|
552
|
+
const sensitiveKeys = [
|
|
553
|
+
"authorization",
|
|
554
|
+
"x-api-key",
|
|
555
|
+
"cookie",
|
|
556
|
+
"set-cookie",
|
|
557
|
+
"x-auth-token"
|
|
558
|
+
];
|
|
559
|
+
const sanitized = {};
|
|
560
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
561
|
+
if (sensitiveKeys.includes(key.toLowerCase())) {
|
|
562
|
+
sanitized[key] = "[REDACTED]";
|
|
563
|
+
} else {
|
|
564
|
+
sanitized[key] = value;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return sanitized;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Safely parse JSON response
|
|
571
|
+
*/
|
|
572
|
+
async safeParseJson(response) {
|
|
573
|
+
const text = await response.text();
|
|
574
|
+
if (!text) {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
try {
|
|
578
|
+
return JSON.parse(text);
|
|
579
|
+
} catch {
|
|
580
|
+
return text;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Merge two abort signals
|
|
585
|
+
*/
|
|
586
|
+
mergeSignals(signal1, signal2) {
|
|
587
|
+
const controller = new AbortController();
|
|
588
|
+
const abort = () => controller.abort();
|
|
589
|
+
signal1.addEventListener("abort", abort);
|
|
590
|
+
signal2.addEventListener("abort", abort);
|
|
591
|
+
if (signal1.aborted || signal2.aborted) {
|
|
592
|
+
controller.abort();
|
|
593
|
+
}
|
|
594
|
+
return controller.signal;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
function generateIdempotencyKey() {
|
|
598
|
+
const timestamp = Date.now().toString(36);
|
|
599
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
600
|
+
return `${timestamp}-${random}`;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/client/filters.schema.ts
|
|
604
|
+
var FILTER_SCHEMA = [
|
|
605
|
+
{
|
|
606
|
+
name: "Platform & Activity",
|
|
607
|
+
description: "Filter by platform and user activity",
|
|
608
|
+
filters: [
|
|
609
|
+
{
|
|
610
|
+
name: "platform",
|
|
611
|
+
description: "Target users on a specific platform",
|
|
612
|
+
type: "string",
|
|
613
|
+
enum: ["all", "farcaster", "twitter"],
|
|
614
|
+
example: "farcaster"
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
name: "activeInLastDays",
|
|
618
|
+
description: "Users who have been active within the last N days",
|
|
619
|
+
type: "number",
|
|
620
|
+
min: 1,
|
|
621
|
+
max: 365,
|
|
622
|
+
example: 30
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: "minCastCount",
|
|
626
|
+
description: "Users with at least N posts/casts",
|
|
627
|
+
type: "number",
|
|
628
|
+
min: 0,
|
|
629
|
+
example: 10
|
|
630
|
+
}
|
|
631
|
+
]
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
name: "Reputation & Quality",
|
|
635
|
+
description: "Filter by user reputation scores",
|
|
636
|
+
filters: [
|
|
637
|
+
{
|
|
638
|
+
name: "neynarScoreMin",
|
|
639
|
+
description: "Minimum Neynar reputation score (0-1, higher = more reputable)",
|
|
640
|
+
type: "number",
|
|
641
|
+
min: 0,
|
|
642
|
+
max: 1,
|
|
643
|
+
example: 0.5
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: "neynarScoreMax",
|
|
647
|
+
description: "Maximum Neynar reputation score",
|
|
648
|
+
type: "number",
|
|
649
|
+
min: 0,
|
|
650
|
+
max: 1,
|
|
651
|
+
example: 0.9
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
name: "quotientScoreMin",
|
|
655
|
+
description: "Minimum Quotient score (0-1, engagement quality)",
|
|
656
|
+
type: "number",
|
|
657
|
+
min: 0,
|
|
658
|
+
max: 1,
|
|
659
|
+
example: 0.5
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
name: "spamLabel",
|
|
663
|
+
description: "Filter by spam classification",
|
|
664
|
+
type: "string",
|
|
665
|
+
enum: ["not_spam_only", "spam_only", "all"],
|
|
666
|
+
example: "not_spam_only"
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
name: "verifiedOnly",
|
|
670
|
+
description: "Only include verified users",
|
|
671
|
+
type: "boolean",
|
|
672
|
+
example: true
|
|
673
|
+
}
|
|
674
|
+
]
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
name: "Social Graph",
|
|
678
|
+
description: "Filter by follower counts and relationships",
|
|
679
|
+
filters: [
|
|
680
|
+
{
|
|
681
|
+
name: "minFollowers",
|
|
682
|
+
description: "Minimum follower count",
|
|
683
|
+
type: "number",
|
|
684
|
+
min: 0,
|
|
685
|
+
example: 1e3
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
name: "maxFollowers",
|
|
689
|
+
description: "Maximum follower count",
|
|
690
|
+
type: "number",
|
|
691
|
+
min: 0,
|
|
692
|
+
example: 1e5
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
name: "followersOf",
|
|
696
|
+
description: "Users who follow a specific FID (Farcaster ID)",
|
|
697
|
+
type: "number",
|
|
698
|
+
example: 3
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
name: "mutualsWith",
|
|
702
|
+
description: "Users who have mutual follows with a specific FID",
|
|
703
|
+
type: "number",
|
|
704
|
+
example: 3
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: "Token Filters",
|
|
710
|
+
description: "Filter by token preferences or holdings. IMPORTANT: Signal tokens and token holders are different concepts.",
|
|
711
|
+
filters: [
|
|
712
|
+
{
|
|
713
|
+
name: "signalTokens",
|
|
714
|
+
description: 'Users who have SET specific tokens on their profile as interests/preferences. Does NOT require actually holding the token - it is a social signal like "I am interested in this community". Use this to find community members by affinity.',
|
|
715
|
+
type: "array",
|
|
716
|
+
items: { name: "tokenAddress", type: "string", description: "Token contract address", example: "0x..." },
|
|
717
|
+
example: ["0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed"]
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
name: "tokenHolders",
|
|
721
|
+
description: "Users who actually HOLD specific tokens in their wallet (verified onchain). Use this to find real token holders with actual balances. Requires wallet verification.",
|
|
722
|
+
type: "array",
|
|
723
|
+
items: {
|
|
724
|
+
name: "tokenConfig",
|
|
725
|
+
type: "object",
|
|
726
|
+
description: "Token holder filter config",
|
|
727
|
+
example: { tokenAddress: "0x...", chainId: 8453 },
|
|
728
|
+
properties: {
|
|
729
|
+
tokenAddress: { name: "tokenAddress", type: "string", description: "Token contract address", example: "0x..." },
|
|
730
|
+
chainId: { name: "chainId", type: "number", description: "Chain ID (1=Ethereum, 8453=Base)", example: 8453 },
|
|
731
|
+
minBalance: { name: "minBalance", type: "string", description: "Minimum balance in wei (e.g., 1000000000000000000 = 1 token with 18 decimals)", example: "1000000000000000000" }
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
example: [{ tokenAddress: "0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed", chainId: 8453 }]
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
name: "hasBaseWallet",
|
|
738
|
+
description: "Users with a wallet on Base chain",
|
|
739
|
+
type: "boolean",
|
|
740
|
+
example: true
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
name: "hasVerifiedWallet",
|
|
744
|
+
description: "Users with a verified wallet address",
|
|
745
|
+
type: "boolean",
|
|
746
|
+
example: true
|
|
747
|
+
}
|
|
748
|
+
]
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
name: "Economics",
|
|
752
|
+
description: "Filter by attention price and battery",
|
|
753
|
+
filters: [
|
|
754
|
+
{
|
|
755
|
+
name: "maxAttentionPriceUsd",
|
|
756
|
+
description: "Maximum attention price in USD",
|
|
757
|
+
type: "number",
|
|
758
|
+
min: 0,
|
|
759
|
+
example: 0.5
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
name: "minBatteryPercentage",
|
|
763
|
+
description: "Minimum battery percentage (0-100)",
|
|
764
|
+
type: "number",
|
|
765
|
+
min: 0,
|
|
766
|
+
max: 100,
|
|
767
|
+
example: 50
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
name: "hasRechargedInLastDays",
|
|
771
|
+
description: "Users who recharged battery within N days",
|
|
772
|
+
type: "number",
|
|
773
|
+
min: 1,
|
|
774
|
+
example: 7
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: "excludePingedToday",
|
|
778
|
+
description: "Exclude users who were already pinged today",
|
|
779
|
+
type: "boolean",
|
|
780
|
+
example: true
|
|
781
|
+
}
|
|
782
|
+
]
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
name: "Geography",
|
|
786
|
+
description: "Filter by location and timezone",
|
|
787
|
+
filters: [
|
|
788
|
+
{
|
|
789
|
+
name: "countries",
|
|
790
|
+
description: "ISO 3166-1 alpha-2 country codes",
|
|
791
|
+
type: "array",
|
|
792
|
+
items: { name: "countryCode", type: "string", description: "Country code", example: "US" },
|
|
793
|
+
example: ["US", "CA", "GB"]
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: "timezones",
|
|
797
|
+
description: "Filter by UTC timezone offset",
|
|
798
|
+
type: "array",
|
|
799
|
+
items: {
|
|
800
|
+
name: "timezone",
|
|
801
|
+
type: "object",
|
|
802
|
+
description: "Timezone configuration",
|
|
803
|
+
example: { offset: -5, range: 2 },
|
|
804
|
+
properties: {
|
|
805
|
+
offset: { name: "offset", type: "number", description: "UTC offset in hours", example: -5 },
|
|
806
|
+
range: { name: "range", type: "number", description: "Range in hours for fuzzy matching", example: 2 }
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
example: [{ offset: -5, range: 2 }]
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
name: "User Selection",
|
|
815
|
+
description: "Target specific users",
|
|
816
|
+
filters: [
|
|
817
|
+
{
|
|
818
|
+
name: "fids",
|
|
819
|
+
description: "Specific Farcaster IDs to target",
|
|
820
|
+
type: "array",
|
|
821
|
+
items: { name: "fid", type: "number", description: "Farcaster ID", example: 3 },
|
|
822
|
+
example: [3, 1234, 5678]
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
name: "userIds",
|
|
826
|
+
description: "Specific user IDs to target",
|
|
827
|
+
type: "array",
|
|
828
|
+
items: { name: "userId", type: "string", description: "User ID", example: "abc123" },
|
|
829
|
+
example: ["abc123", "def456"]
|
|
830
|
+
}
|
|
831
|
+
]
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
name: "Sorting",
|
|
835
|
+
description: "Order results",
|
|
836
|
+
filters: [
|
|
837
|
+
{
|
|
838
|
+
name: "orderBy",
|
|
839
|
+
description: "Sort order for results",
|
|
840
|
+
type: "string",
|
|
841
|
+
enum: [
|
|
842
|
+
"attention_price_asc",
|
|
843
|
+
"attention_price_desc",
|
|
844
|
+
"neynar_score_desc",
|
|
845
|
+
"followers_desc",
|
|
846
|
+
"followers_asc",
|
|
847
|
+
"recent_activity",
|
|
848
|
+
"battery_desc",
|
|
849
|
+
"random"
|
|
850
|
+
],
|
|
851
|
+
example: "attention_price_asc"
|
|
852
|
+
}
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
];
|
|
856
|
+
function getAllFilterNames() {
|
|
857
|
+
return FILTER_SCHEMA.flatMap((cat) => cat.filters.map((f) => f.name));
|
|
858
|
+
}
|
|
859
|
+
function getFilterSchema(name) {
|
|
860
|
+
for (const category of FILTER_SCHEMA) {
|
|
861
|
+
const filter = category.filters.find((f) => f.name === name);
|
|
862
|
+
if (filter) return filter;
|
|
863
|
+
}
|
|
864
|
+
return void 0;
|
|
865
|
+
}
|
|
866
|
+
function generateFilterDocumentation() {
|
|
867
|
+
let doc = "# Available Filters\n\n";
|
|
868
|
+
for (const category of FILTER_SCHEMA) {
|
|
869
|
+
doc += `## ${category.name}
|
|
870
|
+
`;
|
|
871
|
+
doc += `${category.description}
|
|
872
|
+
|
|
873
|
+
`;
|
|
874
|
+
for (const filter of category.filters) {
|
|
875
|
+
doc += `### \`${filter.name}\`
|
|
876
|
+
`;
|
|
877
|
+
doc += `${filter.description}
|
|
878
|
+
`;
|
|
879
|
+
doc += `- Type: \`${filter.type}\`
|
|
880
|
+
`;
|
|
881
|
+
if (filter.enum) {
|
|
882
|
+
doc += `- Values: ${filter.enum.map((v) => `\`${v}\``).join(", ")}
|
|
883
|
+
`;
|
|
884
|
+
}
|
|
885
|
+
if (filter.min !== void 0) doc += `- Min: ${filter.min}
|
|
886
|
+
`;
|
|
887
|
+
if (filter.max !== void 0) doc += `- Max: ${filter.max}
|
|
888
|
+
`;
|
|
889
|
+
doc += `- Example: \`${JSON.stringify(filter.example)}\`
|
|
890
|
+
|
|
891
|
+
`;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return doc;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// src/client/AgentClient.ts
|
|
898
|
+
var ENDPOINTS2 = {
|
|
899
|
+
LOOKUP: "/agent/lookup",
|
|
900
|
+
INTENT: "/agent/intent",
|
|
901
|
+
PRICE: "/agent/price",
|
|
902
|
+
ESTIMATE: "/agent/estimate",
|
|
903
|
+
PREVIEW: "/agent/preview",
|
|
904
|
+
BULK_INTENT: "/agent/bulk-intent"
|
|
905
|
+
};
|
|
906
|
+
var AgentClient = class {
|
|
907
|
+
http;
|
|
908
|
+
// @ts-expect-error Reserved for future use
|
|
909
|
+
_debug;
|
|
910
|
+
constructor(config) {
|
|
911
|
+
if (!config.apiKey) {
|
|
912
|
+
throw BeeperError.validation("API key is required");
|
|
913
|
+
}
|
|
914
|
+
const httpConfig = createHttpConfig({
|
|
915
|
+
apiKey: config.apiKey,
|
|
916
|
+
...config.environment && { environment: config.environment },
|
|
917
|
+
...config.baseUrl && { baseUrl: config.baseUrl },
|
|
918
|
+
...config.timeoutMs && { timeout: config.timeoutMs },
|
|
919
|
+
...config.fetch && { fetch: config.fetch },
|
|
920
|
+
...config.debug && { debug: config.debug }
|
|
921
|
+
});
|
|
922
|
+
this.http = new HttpClient(httpConfig);
|
|
923
|
+
this._debug = config.debug ?? false;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Look up a user by username, FID, or wallet address
|
|
927
|
+
*
|
|
928
|
+
* @param identifier - Username (e.g., "dwr.eth"), FID (e.g., 3), or address
|
|
929
|
+
* @returns User information including wallet address and attention price
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* ```typescript
|
|
933
|
+
* const user = await agent.lookup('dwr.eth');
|
|
934
|
+
* console.log(user.walletAddress); // "0x1234..."
|
|
935
|
+
* console.log(user.attentionPriceUsd); // "0.05"
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
async lookup(identifier) {
|
|
939
|
+
const params = typeof identifier === "number" ? { fid: identifier } : { q: identifier };
|
|
940
|
+
const response = await this.http.get(
|
|
941
|
+
ENDPOINTS2.LOOKUP,
|
|
942
|
+
{ params }
|
|
943
|
+
);
|
|
944
|
+
return response.data.data;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Get the attention price for a user
|
|
948
|
+
*
|
|
949
|
+
* @param identifier - Username, FID, or address
|
|
950
|
+
* @returns Current attention price information
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```typescript
|
|
954
|
+
* const price = await agent.getPrice('dwr.eth');
|
|
955
|
+
* console.log(`Attention price: ${price.priceUsd}`);
|
|
956
|
+
* ```
|
|
957
|
+
*/
|
|
958
|
+
async getPrice(identifier) {
|
|
959
|
+
const params = typeof identifier === "number" ? { fid: identifier } : { q: identifier };
|
|
960
|
+
const response = await this.http.get(
|
|
961
|
+
ENDPOINTS2.PRICE,
|
|
962
|
+
{ params }
|
|
963
|
+
);
|
|
964
|
+
return response.data.data;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Create a payment intent
|
|
968
|
+
*
|
|
969
|
+
* This does NOT execute the payment. It returns the information
|
|
970
|
+
* needed for the user to execute the payment from any wallet.
|
|
971
|
+
*
|
|
972
|
+
* @param input - Payment intent parameters
|
|
973
|
+
* @returns Payment intent with recipient address and amount
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```typescript
|
|
977
|
+
* const intent = await agent.createIntent({
|
|
978
|
+
* to: 'dwr.eth',
|
|
979
|
+
* amount: '$5',
|
|
980
|
+
* message: 'Thanks!',
|
|
981
|
+
* });
|
|
982
|
+
*
|
|
983
|
+
* // Tell the user:
|
|
984
|
+
* console.log(intent.instruction);
|
|
985
|
+
* // "Send 5 USDC to 0x1234... on Base"
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
async createIntent(input) {
|
|
989
|
+
const amountStr = input.amount.replace(/^\$/, "").trim();
|
|
990
|
+
const amountNum = parseFloat(amountStr);
|
|
991
|
+
if (isNaN(amountNum) || amountNum <= 0) {
|
|
992
|
+
throw BeeperError.validation("Amount must be a positive number");
|
|
993
|
+
}
|
|
994
|
+
const response = await this.http.post(
|
|
995
|
+
ENDPOINTS2.INTENT,
|
|
996
|
+
{
|
|
997
|
+
to: input.to,
|
|
998
|
+
amountUsd: amountStr,
|
|
999
|
+
message: input.message,
|
|
1000
|
+
chainId: input.chainId ?? 8453,
|
|
1001
|
+
// Default to Base
|
|
1002
|
+
tokenSymbol: input.tokenSymbol ?? "USDC"
|
|
1003
|
+
}
|
|
1004
|
+
);
|
|
1005
|
+
return response.data.data;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Convenience method to create a human-readable payment instruction
|
|
1009
|
+
*
|
|
1010
|
+
* @param input - Payment intent parameters
|
|
1011
|
+
* @returns Human-readable instruction string
|
|
1012
|
+
*
|
|
1013
|
+
* @example
|
|
1014
|
+
* ```typescript
|
|
1015
|
+
* const instruction = await agent.getPaymentInstruction({
|
|
1016
|
+
* to: 'dwr.eth',
|
|
1017
|
+
* amount: '$5',
|
|
1018
|
+
* });
|
|
1019
|
+
* // "Send 5 USDC to 0x1a2b3c... on Base (to @dwr)"
|
|
1020
|
+
* ```
|
|
1021
|
+
*/
|
|
1022
|
+
async getPaymentInstruction(input) {
|
|
1023
|
+
const intent = await this.createIntent(input);
|
|
1024
|
+
return intent.instruction;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Estimate how many recipients match filters within a budget
|
|
1028
|
+
*
|
|
1029
|
+
* @param input - Filters and budget
|
|
1030
|
+
* @returns Estimate with recipient count, costs, and budget analysis
|
|
1031
|
+
*
|
|
1032
|
+
* @example
|
|
1033
|
+
* ```typescript
|
|
1034
|
+
* const estimate = await agent.estimate({
|
|
1035
|
+
* filters: {
|
|
1036
|
+
* platform: 'farcaster',
|
|
1037
|
+
* minFollowers: 1000,
|
|
1038
|
+
* signalTokens: ['0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'],
|
|
1039
|
+
* },
|
|
1040
|
+
* budget: '$100',
|
|
1041
|
+
* });
|
|
1042
|
+
* console.log(`Can reach ${estimate.recipientCount} people`);
|
|
1043
|
+
* console.log(`Total cost: ${estimate.totalCostUsd}`);
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
async estimate(input) {
|
|
1047
|
+
const budgetStr = input.budget.replace(/^\$/, "").trim();
|
|
1048
|
+
const budgetNum = parseFloat(budgetStr);
|
|
1049
|
+
if (isNaN(budgetNum) || budgetNum <= 0) {
|
|
1050
|
+
throw BeeperError.validation("Budget must be a positive number");
|
|
1051
|
+
}
|
|
1052
|
+
const response = await this.http.post(
|
|
1053
|
+
ENDPOINTS2.ESTIMATE,
|
|
1054
|
+
{
|
|
1055
|
+
filters: input.filters,
|
|
1056
|
+
budgetUsd: budgetStr,
|
|
1057
|
+
message: input.message
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
return response.data.data;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Preview sample users matching filters
|
|
1064
|
+
*
|
|
1065
|
+
* @param input - Filters and limit
|
|
1066
|
+
* @returns Sample users and total count
|
|
1067
|
+
*
|
|
1068
|
+
* @example
|
|
1069
|
+
* ```typescript
|
|
1070
|
+
* const preview = await agent.preview({
|
|
1071
|
+
* filters: { minFollowers: 1000 },
|
|
1072
|
+
* limit: 5,
|
|
1073
|
+
* });
|
|
1074
|
+
* console.log(`Found ${preview.totalCount} users`);
|
|
1075
|
+
* preview.users.forEach(u => {
|
|
1076
|
+
* console.log(`@${u.username} - ${u.priceUsd}`);
|
|
1077
|
+
* });
|
|
1078
|
+
* ```
|
|
1079
|
+
*/
|
|
1080
|
+
async preview(input) {
|
|
1081
|
+
const limit = Math.min(input.limit ?? 10, 20);
|
|
1082
|
+
const response = await this.http.post(
|
|
1083
|
+
ENDPOINTS2.PREVIEW,
|
|
1084
|
+
{
|
|
1085
|
+
filters: input.filters,
|
|
1086
|
+
limit
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
return response.data.data;
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Create bulk payment intents for multiple recipients
|
|
1093
|
+
*
|
|
1094
|
+
* @param input - Filters, budget, and message
|
|
1095
|
+
* @returns Bulk intent with all payment instructions
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```typescript
|
|
1099
|
+
* const bulk = await agent.createBulkIntent({
|
|
1100
|
+
* filters: { signalTokens: ['0x...'] },
|
|
1101
|
+
* budget: '$100',
|
|
1102
|
+
* message: 'GM holders!',
|
|
1103
|
+
* });
|
|
1104
|
+
* console.log(bulk.summary);
|
|
1105
|
+
* // "Send to 50 recipients for total 45.00 USDC on Base"
|
|
1106
|
+
* ```
|
|
1107
|
+
*/
|
|
1108
|
+
async createBulkIntent(input) {
|
|
1109
|
+
const budgetStr = input.budget.replace(/^\$/, "").trim();
|
|
1110
|
+
const budgetNum = parseFloat(budgetStr);
|
|
1111
|
+
if (isNaN(budgetNum) || budgetNum <= 0) {
|
|
1112
|
+
throw BeeperError.validation("Budget must be a positive number");
|
|
1113
|
+
}
|
|
1114
|
+
const response = await this.http.post(
|
|
1115
|
+
ENDPOINTS2.BULK_INTENT,
|
|
1116
|
+
{
|
|
1117
|
+
filters: input.filters,
|
|
1118
|
+
budgetUsd: budgetStr,
|
|
1119
|
+
message: input.message,
|
|
1120
|
+
chainId: input.chainId ?? 8453
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
return response.data.data;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Get filter schema documentation for LLMs
|
|
1127
|
+
*
|
|
1128
|
+
* Returns structured information about all available filters
|
|
1129
|
+
* that can be used in system prompts or tool descriptions.
|
|
1130
|
+
*
|
|
1131
|
+
* @returns Filter schema categories
|
|
1132
|
+
*
|
|
1133
|
+
* @example
|
|
1134
|
+
* ```typescript
|
|
1135
|
+
* const schema = agent.describeFilters();
|
|
1136
|
+
* // Use in LLM prompt:
|
|
1137
|
+
* // "Available filters: " + JSON.stringify(schema)
|
|
1138
|
+
* ```
|
|
1139
|
+
*/
|
|
1140
|
+
describeFilters() {
|
|
1141
|
+
return FILTER_SCHEMA;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Get filter documentation as markdown
|
|
1145
|
+
*
|
|
1146
|
+
* Useful for including in LLM system prompts.
|
|
1147
|
+
*
|
|
1148
|
+
* @returns Markdown documentation of all filters
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* ```typescript
|
|
1152
|
+
* const docs = agent.getFilterDocumentation();
|
|
1153
|
+
* // Include in system prompt for LLM
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
getFilterDocumentation() {
|
|
1157
|
+
return generateFilterDocumentation();
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
// src/client/auth.ts
|
|
1162
|
+
var API_KEY_PREFIXES = {
|
|
1163
|
+
LIVE: "bpk_live_",
|
|
1164
|
+
TEST: "bpk_test_"
|
|
1165
|
+
};
|
|
1166
|
+
function isValidApiKeyFormat(apiKey) {
|
|
1167
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
const hasValidPrefix = apiKey.startsWith(API_KEY_PREFIXES.LIVE) || apiKey.startsWith(API_KEY_PREFIXES.TEST);
|
|
1171
|
+
if (!hasValidPrefix) {
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
const minLength = API_KEY_PREFIXES.LIVE.length + 20;
|
|
1175
|
+
return apiKey.length >= minLength;
|
|
1176
|
+
}
|
|
1177
|
+
function getApiKeyEnvironment(apiKey) {
|
|
1178
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
if (apiKey.startsWith(API_KEY_PREFIXES.LIVE)) {
|
|
1182
|
+
return "production";
|
|
1183
|
+
}
|
|
1184
|
+
if (apiKey.startsWith(API_KEY_PREFIXES.TEST)) {
|
|
1185
|
+
return "test";
|
|
1186
|
+
}
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
function createAuthorizationHeader(apiKey) {
|
|
1190
|
+
return `Bearer ${apiKey}`;
|
|
1191
|
+
}
|
|
1192
|
+
function maskApiKey(apiKey) {
|
|
1193
|
+
if (!apiKey || apiKey.length < 12) {
|
|
1194
|
+
return "***";
|
|
1195
|
+
}
|
|
1196
|
+
const prefix = apiKey.startsWith(API_KEY_PREFIXES.LIVE) ? API_KEY_PREFIXES.LIVE : apiKey.startsWith(API_KEY_PREFIXES.TEST) ? API_KEY_PREFIXES.TEST : "";
|
|
1197
|
+
const suffix = apiKey.slice(-4);
|
|
1198
|
+
return `${prefix}***${suffix}`;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// src/client/BeeperClient.ts
|
|
1202
|
+
var QuoteSchema = zod.z.object({
|
|
1203
|
+
id: zod.z.string(),
|
|
1204
|
+
status: zod.z.enum([
|
|
1205
|
+
"pending",
|
|
1206
|
+
"deposit_confirmed",
|
|
1207
|
+
"executing",
|
|
1208
|
+
"completed",
|
|
1209
|
+
"expired",
|
|
1210
|
+
"failed"
|
|
1211
|
+
]),
|
|
1212
|
+
recipientCount: zod.z.number(),
|
|
1213
|
+
totalAmount: zod.z.string(),
|
|
1214
|
+
protocolFee: zod.z.string(),
|
|
1215
|
+
depositAmount: zod.z.string(),
|
|
1216
|
+
depositAddress: zod.z.string(),
|
|
1217
|
+
depositChainId: zod.z.number(),
|
|
1218
|
+
depositTokenAddress: zod.z.string(),
|
|
1219
|
+
expiresAt: zod.z.string(),
|
|
1220
|
+
input: zod.z.object({
|
|
1221
|
+
filter: zod.z.record(zod.z.unknown()),
|
|
1222
|
+
tokenAddress: zod.z.string(),
|
|
1223
|
+
chainId: zod.z.number(),
|
|
1224
|
+
amountPerRecipient: zod.z.string(),
|
|
1225
|
+
budgetCap: zod.z.string(),
|
|
1226
|
+
memo: zod.z.string().optional(),
|
|
1227
|
+
metadata: zod.z.record(zod.z.unknown()).optional()
|
|
1228
|
+
}),
|
|
1229
|
+
createdAt: zod.z.string(),
|
|
1230
|
+
updatedAt: zod.z.string()
|
|
1231
|
+
});
|
|
1232
|
+
var AttentionQuoteSchema = zod.z.object({
|
|
1233
|
+
id: zod.z.string(),
|
|
1234
|
+
status: zod.z.enum([
|
|
1235
|
+
"pending",
|
|
1236
|
+
"deposit_confirmed",
|
|
1237
|
+
"executing",
|
|
1238
|
+
"completed",
|
|
1239
|
+
"expired",
|
|
1240
|
+
"failed"
|
|
1241
|
+
]),
|
|
1242
|
+
recipientCount: zod.z.number(),
|
|
1243
|
+
expiresAt: zod.z.string(),
|
|
1244
|
+
input: zod.z.record(zod.z.unknown()),
|
|
1245
|
+
createdAt: zod.z.string(),
|
|
1246
|
+
updatedAt: zod.z.string()
|
|
1247
|
+
}).passthrough();
|
|
1248
|
+
var ConfirmResultSchema = zod.z.object({
|
|
1249
|
+
quoteId: zod.z.string(),
|
|
1250
|
+
status: zod.z.enum(["confirmed", "pending_verification"]),
|
|
1251
|
+
detectedAmount: zod.z.string(),
|
|
1252
|
+
sufficient: zod.z.boolean(),
|
|
1253
|
+
blockNumber: zod.z.number(),
|
|
1254
|
+
confirmedAt: zod.z.string()
|
|
1255
|
+
});
|
|
1256
|
+
var ExecuteResultSchema = zod.z.object({
|
|
1257
|
+
quoteId: zod.z.string(),
|
|
1258
|
+
status: zod.z.enum(["executing", "queued"]),
|
|
1259
|
+
estimatedCompletionAt: zod.z.string(),
|
|
1260
|
+
batchId: zod.z.string()
|
|
1261
|
+
});
|
|
1262
|
+
var ReceiptSchema = zod.z.object({
|
|
1263
|
+
quoteId: zod.z.string(),
|
|
1264
|
+
status: zod.z.enum(["completed", "partial", "failed"]),
|
|
1265
|
+
successCount: zod.z.number(),
|
|
1266
|
+
failureCount: zod.z.number(),
|
|
1267
|
+
totalSent: zod.z.string(),
|
|
1268
|
+
refundAmount: zod.z.string(),
|
|
1269
|
+
refundTxHash: zod.z.string().optional(),
|
|
1270
|
+
transactions: zod.z.array(
|
|
1271
|
+
zod.z.object({
|
|
1272
|
+
recipient: zod.z.string(),
|
|
1273
|
+
amount: zod.z.string(),
|
|
1274
|
+
txHash: zod.z.string().nullable(),
|
|
1275
|
+
status: zod.z.enum(["success", "failed"]),
|
|
1276
|
+
error: zod.z.string().optional()
|
|
1277
|
+
})
|
|
1278
|
+
),
|
|
1279
|
+
completedAt: zod.z.string()
|
|
1280
|
+
});
|
|
1281
|
+
var HealthSchema = zod.z.object({
|
|
1282
|
+
status: zod.z.enum(["healthy", "degraded", "unhealthy"]),
|
|
1283
|
+
version: zod.z.string(),
|
|
1284
|
+
timestamp: zod.z.string(),
|
|
1285
|
+
services: zod.z.object({
|
|
1286
|
+
database: zod.z.enum(["up", "down"]),
|
|
1287
|
+
queue: zod.z.enum(["up", "down"]),
|
|
1288
|
+
oracle: zod.z.enum(["up", "down"])
|
|
1289
|
+
})
|
|
1290
|
+
});
|
|
1291
|
+
var ENDPOINTS3 = {
|
|
1292
|
+
QUOTES: "/api/v1/sdk/send/quotes",
|
|
1293
|
+
HEALTH: "/api/v1/sdk/send/health"
|
|
1294
|
+
};
|
|
1295
|
+
var BeeperClient = class {
|
|
1296
|
+
http;
|
|
1297
|
+
constructor(config) {
|
|
1298
|
+
this.validateConfig(config);
|
|
1299
|
+
const httpConfig = createHttpConfig({
|
|
1300
|
+
apiKey: config.apiKey,
|
|
1301
|
+
...config.environment !== void 0 && { environment: config.environment },
|
|
1302
|
+
...config.baseUrl !== void 0 && { baseUrl: config.baseUrl },
|
|
1303
|
+
...config.timeoutMs !== void 0 && { timeout: config.timeoutMs },
|
|
1304
|
+
...config.fetch !== void 0 && { fetch: config.fetch },
|
|
1305
|
+
...config.debug !== void 0 && { debug: config.debug }
|
|
1306
|
+
});
|
|
1307
|
+
this.http = new HttpClient(httpConfig);
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Validates client configuration
|
|
1311
|
+
*/
|
|
1312
|
+
validateConfig(config) {
|
|
1313
|
+
if (!config.apiKey) {
|
|
1314
|
+
throw BeeperError.validation("API key is required");
|
|
1315
|
+
}
|
|
1316
|
+
if (!isValidApiKeyFormat(config.apiKey)) {
|
|
1317
|
+
throw BeeperError.validation(
|
|
1318
|
+
"Invalid API key format. Key must start with bpk_live_ or bpk_test_"
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Creates a local draft. Does NOT make a network request.
|
|
1324
|
+
* @param input - The draft input parameters
|
|
1325
|
+
* @returns A draft object with a generated ID
|
|
1326
|
+
*/
|
|
1327
|
+
createDraft(input) {
|
|
1328
|
+
this.validateDraftInput(input);
|
|
1329
|
+
return {
|
|
1330
|
+
id: `draft_${generateIdempotencyKey()}`,
|
|
1331
|
+
input,
|
|
1332
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Validates draft input parameters
|
|
1337
|
+
*/
|
|
1338
|
+
validateDraftInput(input) {
|
|
1339
|
+
if (!input.filter || typeof input.filter !== "object") {
|
|
1340
|
+
throw BeeperError.validation("Filter is required and must be an object");
|
|
1341
|
+
}
|
|
1342
|
+
if (!input.tokenAddress || !/^0x[a-fA-F0-9]{40}$/.test(input.tokenAddress)) {
|
|
1343
|
+
throw BeeperError.validation("Invalid token address format");
|
|
1344
|
+
}
|
|
1345
|
+
if (!input.chainId || typeof input.chainId !== "number") {
|
|
1346
|
+
throw BeeperError.validation("Chain ID is required and must be a number");
|
|
1347
|
+
}
|
|
1348
|
+
if (!input.amountPerRecipient || !/^[0-9]+$/.test(input.amountPerRecipient)) {
|
|
1349
|
+
throw BeeperError.validation(
|
|
1350
|
+
"Amount per recipient must be a positive integer string"
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
if (!input.budgetCap || !/^[0-9]+$/.test(input.budgetCap)) {
|
|
1354
|
+
throw BeeperError.validation("Budget cap must be a positive integer string");
|
|
1355
|
+
}
|
|
1356
|
+
if (input.memo !== void 0 && input.memo.length > 256) {
|
|
1357
|
+
throw BeeperError.validation("Memo must be 256 characters or less");
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Submits draft to server and receives a priced quote
|
|
1362
|
+
* @param draft - The draft to quote
|
|
1363
|
+
* @param opts - Optional quote options
|
|
1364
|
+
* @returns A quote with server-computed pricing
|
|
1365
|
+
*/
|
|
1366
|
+
async createQuote(draft, opts) {
|
|
1367
|
+
const body = {
|
|
1368
|
+
...draft.input,
|
|
1369
|
+
ttlSeconds: opts?.ttlSeconds ?? 300
|
|
1370
|
+
};
|
|
1371
|
+
const response = await this.http.post(
|
|
1372
|
+
ENDPOINTS3.QUOTES,
|
|
1373
|
+
body,
|
|
1374
|
+
{},
|
|
1375
|
+
QuoteSchema
|
|
1376
|
+
);
|
|
1377
|
+
return response.data;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Creates an attention marketplace quote (message-based send)
|
|
1381
|
+
* @param input - Attention quote input with message, recipients, and budget
|
|
1382
|
+
* @param opts - Optional quote options
|
|
1383
|
+
* @returns An attention quote with server-computed pricing
|
|
1384
|
+
*
|
|
1385
|
+
* @example
|
|
1386
|
+
* ```typescript
|
|
1387
|
+
* const quote = await client.createAttentionQuote({
|
|
1388
|
+
* message: "Check out our new feature!",
|
|
1389
|
+
* filter: FilterBuilder.and([
|
|
1390
|
+
* FilterBuilder.platform('farcaster'),
|
|
1391
|
+
* FilterBuilder.activeInLastDays(7),
|
|
1392
|
+
* ]).toJSON(),
|
|
1393
|
+
* budgetUSD: "50.00",
|
|
1394
|
+
* rewardType: 'guaranteed',
|
|
1395
|
+
* });
|
|
1396
|
+
* ```
|
|
1397
|
+
*/
|
|
1398
|
+
async createAttentionQuote(input, opts) {
|
|
1399
|
+
if (!input.message || input.message.length === 0) {
|
|
1400
|
+
throw BeeperError.validation("Message is required");
|
|
1401
|
+
}
|
|
1402
|
+
if (input.message.length > 1e3) {
|
|
1403
|
+
throw BeeperError.validation("Message must be 1000 characters or less");
|
|
1404
|
+
}
|
|
1405
|
+
if (!input.recipientFids?.length && !input.filter) {
|
|
1406
|
+
throw BeeperError.validation("Must provide either recipientFids or filter");
|
|
1407
|
+
}
|
|
1408
|
+
if (input.recipientFids && input.recipientFids.length > 1e3) {
|
|
1409
|
+
throw BeeperError.validation("recipientFids cannot exceed 1000");
|
|
1410
|
+
}
|
|
1411
|
+
if (!input.budgetUSD || !/^[0-9]+\.?[0-9]*$/.test(input.budgetUSD)) {
|
|
1412
|
+
throw BeeperError.validation("budgetUSD must be a valid USD amount");
|
|
1413
|
+
}
|
|
1414
|
+
if (parseFloat(input.budgetUSD) <= 0) {
|
|
1415
|
+
throw BeeperError.validation("budgetUSD must be positive");
|
|
1416
|
+
}
|
|
1417
|
+
const body = {
|
|
1418
|
+
message: input.message,
|
|
1419
|
+
recipientFids: input.recipientFids,
|
|
1420
|
+
filter: input.filter,
|
|
1421
|
+
budgetUSD: input.budgetUSD,
|
|
1422
|
+
rewardType: input.rewardType ?? "guaranteed",
|
|
1423
|
+
memo: input.memo,
|
|
1424
|
+
metadata: input.metadata,
|
|
1425
|
+
ttlSeconds: opts?.ttlSeconds ?? 300
|
|
1426
|
+
};
|
|
1427
|
+
const response = await this.http.post(
|
|
1428
|
+
ENDPOINTS3.QUOTES,
|
|
1429
|
+
body,
|
|
1430
|
+
{},
|
|
1431
|
+
AttentionQuoteSchema
|
|
1432
|
+
);
|
|
1433
|
+
return response.data;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Retrieves an existing quote by ID
|
|
1437
|
+
* @param quoteId - The quote ID
|
|
1438
|
+
* @returns The quote
|
|
1439
|
+
*/
|
|
1440
|
+
async getQuote(quoteId) {
|
|
1441
|
+
const response = await this.http.get(
|
|
1442
|
+
`${ENDPOINTS3.QUOTES}/${quoteId}`,
|
|
1443
|
+
{},
|
|
1444
|
+
QuoteSchema
|
|
1445
|
+
);
|
|
1446
|
+
return response.data;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Confirms that deposit has been made
|
|
1450
|
+
* @param quoteId - The quote ID
|
|
1451
|
+
* @param params - Confirmation parameters including txHash and idempotencyKey
|
|
1452
|
+
* @returns Confirmation result
|
|
1453
|
+
*/
|
|
1454
|
+
async confirmDeposit(quoteId, params) {
|
|
1455
|
+
if (!params.idempotencyKey) {
|
|
1456
|
+
throw BeeperError.validation("Idempotency key is required for confirmDeposit");
|
|
1457
|
+
}
|
|
1458
|
+
if (!params.txHash || !/^0x[a-fA-F0-9]{64}$/.test(params.txHash)) {
|
|
1459
|
+
throw BeeperError.validation("Invalid transaction hash format");
|
|
1460
|
+
}
|
|
1461
|
+
const response = await this.http.post(
|
|
1462
|
+
`${ENDPOINTS3.QUOTES}/${quoteId}/confirm`,
|
|
1463
|
+
{ txHash: params.txHash },
|
|
1464
|
+
{ idempotencyKey: params.idempotencyKey },
|
|
1465
|
+
ConfirmResultSchema
|
|
1466
|
+
);
|
|
1467
|
+
return response.data;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Triggers execution of the send
|
|
1471
|
+
* @param quoteId - The quote ID
|
|
1472
|
+
* @param params - Execution parameters including idempotencyKey
|
|
1473
|
+
* @returns Execution result
|
|
1474
|
+
*/
|
|
1475
|
+
async executeSend(quoteId, params) {
|
|
1476
|
+
if (!params.idempotencyKey) {
|
|
1477
|
+
throw BeeperError.validation("Idempotency key is required for executeSend");
|
|
1478
|
+
}
|
|
1479
|
+
const response = await this.http.post(
|
|
1480
|
+
`${ENDPOINTS3.QUOTES}/${quoteId}/execute`,
|
|
1481
|
+
{},
|
|
1482
|
+
{ idempotencyKey: params.idempotencyKey },
|
|
1483
|
+
ExecuteResultSchema
|
|
1484
|
+
);
|
|
1485
|
+
return response.data;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Gets the receipt for a completed (or failed) execution
|
|
1489
|
+
* @param quoteId - The quote ID
|
|
1490
|
+
* @returns The receipt
|
|
1491
|
+
*/
|
|
1492
|
+
async getReceiptByQuoteId(quoteId) {
|
|
1493
|
+
const response = await this.http.get(
|
|
1494
|
+
`${ENDPOINTS3.QUOTES}/${quoteId}/receipt`,
|
|
1495
|
+
{},
|
|
1496
|
+
ReceiptSchema
|
|
1497
|
+
);
|
|
1498
|
+
return response.data;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Polls until execution completes, then returns receipt
|
|
1502
|
+
* @param quoteId - The quote ID
|
|
1503
|
+
* @param opts - Polling options
|
|
1504
|
+
* @returns The receipt when execution completes
|
|
1505
|
+
*/
|
|
1506
|
+
async pollUntilCompleteByQuoteId(quoteId, opts) {
|
|
1507
|
+
const maxAttempts = opts?.maxAttempts ?? 60;
|
|
1508
|
+
const intervalMs = opts?.intervalMs ?? 2e3;
|
|
1509
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1510
|
+
const quote = await this.getQuote(quoteId);
|
|
1511
|
+
if (opts?.onPoll) {
|
|
1512
|
+
opts.onPoll(quote);
|
|
1513
|
+
}
|
|
1514
|
+
if (quote.status === "completed" || quote.status === "failed") {
|
|
1515
|
+
return this.getReceiptByQuoteId(quoteId);
|
|
1516
|
+
}
|
|
1517
|
+
if (quote.status === "expired") {
|
|
1518
|
+
throw new BeeperError({
|
|
1519
|
+
code: ErrorCodes.QUOTE_EXPIRED,
|
|
1520
|
+
message: `Quote ${quoteId} expired while polling`,
|
|
1521
|
+
retryable: false
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
if (attempt < maxAttempts - 1) {
|
|
1525
|
+
await this.sleep(intervalMs);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
throw new BeeperError({
|
|
1529
|
+
code: ErrorCodes.TIMEOUT,
|
|
1530
|
+
message: `Polling timed out after ${maxAttempts} attempts`,
|
|
1531
|
+
retryable: false
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Checks API health status
|
|
1536
|
+
* @returns Health status
|
|
1537
|
+
*/
|
|
1538
|
+
async health() {
|
|
1539
|
+
const response = await this.http.get(
|
|
1540
|
+
ENDPOINTS3.HEALTH,
|
|
1541
|
+
{},
|
|
1542
|
+
HealthSchema
|
|
1543
|
+
);
|
|
1544
|
+
return response.data;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Sleep helper for polling
|
|
1548
|
+
*/
|
|
1549
|
+
sleep(ms) {
|
|
1550
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
var BeeperClient_default = BeeperClient;
|
|
1554
|
+
var LegacyFilterOperatorSchema = zod.z.enum([
|
|
1555
|
+
"eq",
|
|
1556
|
+
"ne",
|
|
1557
|
+
"gt",
|
|
1558
|
+
"gte",
|
|
1559
|
+
"lt",
|
|
1560
|
+
"lte",
|
|
1561
|
+
"in",
|
|
1562
|
+
"nin",
|
|
1563
|
+
"contains",
|
|
1564
|
+
"startsWith",
|
|
1565
|
+
"endsWith"
|
|
1566
|
+
]);
|
|
1567
|
+
var LegacyFilterValueSchema = zod.z.union([
|
|
1568
|
+
zod.z.string(),
|
|
1569
|
+
zod.z.number(),
|
|
1570
|
+
zod.z.boolean(),
|
|
1571
|
+
zod.z.array(zod.z.string()),
|
|
1572
|
+
zod.z.array(zod.z.number())
|
|
1573
|
+
]);
|
|
1574
|
+
var LegacyFieldComparisonSchema = zod.z.object({
|
|
1575
|
+
field: zod.z.string().min(1),
|
|
1576
|
+
op: LegacyFilterOperatorSchema,
|
|
1577
|
+
value: LegacyFilterValueSchema
|
|
1578
|
+
});
|
|
1579
|
+
var LegacyRecipientFilterDSLSchema = zod.z.lazy(
|
|
1580
|
+
() => zod.z.union([
|
|
1581
|
+
// Match all
|
|
1582
|
+
zod.z.object({ all: zod.z.literal(true) }),
|
|
1583
|
+
// Logical AND
|
|
1584
|
+
zod.z.object({ and: zod.z.array(LegacyRecipientFilterDSLSchema).min(1) }),
|
|
1585
|
+
// Logical OR
|
|
1586
|
+
zod.z.object({ or: zod.z.array(LegacyRecipientFilterDSLSchema).min(1) }),
|
|
1587
|
+
// Logical NOT
|
|
1588
|
+
zod.z.object({ not: LegacyRecipientFilterDSLSchema }),
|
|
1589
|
+
// Field comparison
|
|
1590
|
+
LegacyFieldComparisonSchema
|
|
1591
|
+
])
|
|
1592
|
+
);
|
|
1593
|
+
var FilterOperatorSchema = LegacyFilterOperatorSchema;
|
|
1594
|
+
var FilterValueSchema = LegacyFilterValueSchema;
|
|
1595
|
+
var FieldComparisonSchema = LegacyFieldComparisonSchema;
|
|
1596
|
+
var PlatformFilterSchema = zod.z.object({
|
|
1597
|
+
platform: zod.z.enum(["all", "farcaster", "twitter"])
|
|
1598
|
+
});
|
|
1599
|
+
var SpecificUsersFilterSchema = zod.z.object({
|
|
1600
|
+
specificIds: zod.z.array(zod.z.string()).optional(),
|
|
1601
|
+
specificFids: zod.z.array(zod.z.number().int()).optional(),
|
|
1602
|
+
specificUsernames: zod.z.array(zod.z.string()).optional(),
|
|
1603
|
+
specificUsersMode: zod.z.enum(["exclusive", "additive"]).optional()
|
|
1604
|
+
});
|
|
1605
|
+
var ExcludeUsersFilterSchema = zod.z.object({
|
|
1606
|
+
excludeFids: zod.z.array(zod.z.number().int()).optional(),
|
|
1607
|
+
excludeUsernames: zod.z.array(zod.z.string()).optional()
|
|
1608
|
+
});
|
|
1609
|
+
var MinFollowersFilterSchema = zod.z.object({
|
|
1610
|
+
minFollowers: zod.z.number().int().min(0)
|
|
1611
|
+
});
|
|
1612
|
+
var MaxFollowersFilterSchema = zod.z.object({
|
|
1613
|
+
maxFollowers: zod.z.number().int().min(0)
|
|
1614
|
+
});
|
|
1615
|
+
var MinFollowingFilterSchema = zod.z.object({
|
|
1616
|
+
minFollowing: zod.z.number().int().min(0)
|
|
1617
|
+
});
|
|
1618
|
+
var MaxFollowingFilterSchema = zod.z.object({
|
|
1619
|
+
maxFollowing: zod.z.number().int().min(0)
|
|
1620
|
+
});
|
|
1621
|
+
var FollowersOfFilterSchema = zod.z.object({
|
|
1622
|
+
followersOf: zod.z.number().int()
|
|
1623
|
+
});
|
|
1624
|
+
var FollowingOfFilterSchema = zod.z.object({
|
|
1625
|
+
followingOf: zod.z.number().int()
|
|
1626
|
+
});
|
|
1627
|
+
var MutualsWithFilterSchema = zod.z.object({
|
|
1628
|
+
mutualsWith: zod.z.number().int()
|
|
1629
|
+
});
|
|
1630
|
+
var SignalTokenFilterSchema = zod.z.object({
|
|
1631
|
+
signalTokens: zod.z.array(
|
|
1632
|
+
zod.z.object({
|
|
1633
|
+
tokenAddress: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid token address")
|
|
1634
|
+
})
|
|
1635
|
+
)
|
|
1636
|
+
});
|
|
1637
|
+
var TimezoneFilterSchema = zod.z.object({
|
|
1638
|
+
timezones: zod.z.array(
|
|
1639
|
+
zod.z.object({
|
|
1640
|
+
offset: zod.z.number(),
|
|
1641
|
+
// UTC offset in hours (e.g., -5, +9, +5.5)
|
|
1642
|
+
range: zod.z.number().min(0).max(12).optional()
|
|
1643
|
+
// Range in hours (+/-)
|
|
1644
|
+
})
|
|
1645
|
+
)
|
|
1646
|
+
});
|
|
1647
|
+
var CountryFilterSchema = zod.z.object({
|
|
1648
|
+
countries: zod.z.array(
|
|
1649
|
+
zod.z.object({
|
|
1650
|
+
code: zod.z.string().length(2)
|
|
1651
|
+
// ISO 3166-1 alpha-2 country code
|
|
1652
|
+
})
|
|
1653
|
+
)
|
|
1654
|
+
});
|
|
1655
|
+
var RolesFilterSchema = zod.z.object({
|
|
1656
|
+
roles: zod.z.array(zod.z.string())
|
|
1657
|
+
});
|
|
1658
|
+
var SocialFilterSchema = zod.z.object({
|
|
1659
|
+
specificIds: zod.z.array(zod.z.string()).optional(),
|
|
1660
|
+
specificFids: zod.z.array(zod.z.number().int()).optional(),
|
|
1661
|
+
specificUsernames: zod.z.array(zod.z.string()).optional(),
|
|
1662
|
+
specificUsersMode: zod.z.enum(["exclusive", "additive"]).optional(),
|
|
1663
|
+
excludeFids: zod.z.array(zod.z.number().int()).optional(),
|
|
1664
|
+
excludeUsernames: zod.z.array(zod.z.string()).optional(),
|
|
1665
|
+
followersOf: zod.z.number().int().optional(),
|
|
1666
|
+
followingOf: zod.z.number().int().optional(),
|
|
1667
|
+
mutualsWith: zod.z.number().int().optional(),
|
|
1668
|
+
minFollowers: zod.z.number().int().min(0).optional(),
|
|
1669
|
+
maxFollowers: zod.z.number().int().min(0).optional(),
|
|
1670
|
+
minFollowing: zod.z.number().int().min(0).optional(),
|
|
1671
|
+
maxFollowing: zod.z.number().int().min(0).optional(),
|
|
1672
|
+
signalTokens: zod.z.array(
|
|
1673
|
+
zod.z.object({
|
|
1674
|
+
tokenAddress: zod.z.string()
|
|
1675
|
+
})
|
|
1676
|
+
).optional(),
|
|
1677
|
+
timezones: zod.z.array(
|
|
1678
|
+
zod.z.object({
|
|
1679
|
+
offset: zod.z.number(),
|
|
1680
|
+
range: zod.z.number().min(0).max(12).optional()
|
|
1681
|
+
})
|
|
1682
|
+
).optional(),
|
|
1683
|
+
countries: zod.z.array(
|
|
1684
|
+
zod.z.object({
|
|
1685
|
+
code: zod.z.string()
|
|
1686
|
+
})
|
|
1687
|
+
).optional(),
|
|
1688
|
+
roles: zod.z.array(zod.z.string()).optional()
|
|
1689
|
+
});
|
|
1690
|
+
var ActiveInLastDaysFilterSchema = zod.z.object({
|
|
1691
|
+
activeInLastDays: zod.z.number().int().min(1).max(365)
|
|
1692
|
+
});
|
|
1693
|
+
var NeynarScoreMinFilterSchema = zod.z.object({
|
|
1694
|
+
neynarScoreMin: zod.z.number().min(0).max(1)
|
|
1695
|
+
});
|
|
1696
|
+
var NeynarScoreMaxFilterSchema = zod.z.object({
|
|
1697
|
+
neynarScoreMax: zod.z.number().min(0).max(1)
|
|
1698
|
+
});
|
|
1699
|
+
var QuotientScoreMinFilterSchema = zod.z.object({
|
|
1700
|
+
quotientScoreMin: zod.z.number().min(0).max(1)
|
|
1701
|
+
});
|
|
1702
|
+
var QuotientScoreMaxFilterSchema = zod.z.object({
|
|
1703
|
+
quotientScoreMax: zod.z.number().min(0).max(1)
|
|
1704
|
+
});
|
|
1705
|
+
var SpamLabelFilterSchema = zod.z.object({
|
|
1706
|
+
spamLabel: zod.z.enum(["not_spam_only", "spam_only", "all"])
|
|
1707
|
+
});
|
|
1708
|
+
var ProSubscriptionFilterSchema = zod.z.object({
|
|
1709
|
+
proSubscriptionRequired: zod.z.boolean()
|
|
1710
|
+
});
|
|
1711
|
+
var MinProTenureDaysFilterSchema = zod.z.object({
|
|
1712
|
+
minProTenureDays: zod.z.number().int().min(0)
|
|
1713
|
+
});
|
|
1714
|
+
var MinTenureDaysFilterSchema = zod.z.object({
|
|
1715
|
+
minTenureDays: zod.z.number().int().min(0)
|
|
1716
|
+
});
|
|
1717
|
+
var VerifiedOnlyFilterSchema = zod.z.object({
|
|
1718
|
+
verifiedOnly: zod.z.boolean()
|
|
1719
|
+
});
|
|
1720
|
+
var ReputationFilterSchema = zod.z.object({
|
|
1721
|
+
neynarScoreMin: zod.z.number().min(0).max(1).optional(),
|
|
1722
|
+
neynarScoreMax: zod.z.number().min(0).max(1).optional(),
|
|
1723
|
+
quotientScoreMin: zod.z.number().min(0).max(1).optional(),
|
|
1724
|
+
quotientScoreMax: zod.z.number().min(0).max(1).optional(),
|
|
1725
|
+
spamLabel: zod.z.enum(["not_spam_only", "spam_only", "all"]).optional(),
|
|
1726
|
+
proSubscriptionRequired: zod.z.boolean().optional(),
|
|
1727
|
+
minProTenureDays: zod.z.number().int().min(0).optional(),
|
|
1728
|
+
minTenureDays: zod.z.number().int().min(0).optional(),
|
|
1729
|
+
verifiedOnly: zod.z.boolean().optional()
|
|
1730
|
+
});
|
|
1731
|
+
var MaxAttentionPriceFilterSchema = zod.z.object({
|
|
1732
|
+
maxAttentionPriceUsd: zod.z.number().positive()
|
|
1733
|
+
});
|
|
1734
|
+
var MinAttentionPriceFilterSchema = zod.z.object({
|
|
1735
|
+
minAttentionPriceUsd: zod.z.number().min(0)
|
|
1736
|
+
});
|
|
1737
|
+
var MinFidFilterSchema = zod.z.object({
|
|
1738
|
+
minFid: zod.z.number().int().min(1)
|
|
1739
|
+
});
|
|
1740
|
+
var MaxFidFilterSchema = zod.z.object({
|
|
1741
|
+
maxFid: zod.z.number().int().min(1)
|
|
1742
|
+
});
|
|
1743
|
+
var MinBatteryPercentageFilterSchema = zod.z.object({
|
|
1744
|
+
minBatteryPercentage: zod.z.number().min(0).max(100)
|
|
1745
|
+
});
|
|
1746
|
+
var ExcludePingedTodayFilterSchema = zod.z.object({
|
|
1747
|
+
excludePingedToday: zod.z.boolean()
|
|
1748
|
+
});
|
|
1749
|
+
var HasRechargedInLastDaysFilterSchema = zod.z.object({
|
|
1750
|
+
hasRechargedInLastDays: zod.z.number().int().min(1)
|
|
1751
|
+
});
|
|
1752
|
+
var RequireLotteryOptInFilterSchema = zod.z.object({
|
|
1753
|
+
requireLotteryOptIn: zod.z.boolean()
|
|
1754
|
+
});
|
|
1755
|
+
var RequireQuizOptInFilterSchema = zod.z.object({
|
|
1756
|
+
requireQuizOptIn: zod.z.boolean()
|
|
1757
|
+
});
|
|
1758
|
+
var MinCastCountFilterSchema = zod.z.object({
|
|
1759
|
+
minCastCount: zod.z.number().int().min(0)
|
|
1760
|
+
});
|
|
1761
|
+
var MinClickThroughRateFilterSchema = zod.z.object({
|
|
1762
|
+
minClickThroughRate: zod.z.number().min(0).max(1)
|
|
1763
|
+
});
|
|
1764
|
+
var IsWaitlistedFilterSchema = zod.z.object({
|
|
1765
|
+
isWaitlisted: zod.z.boolean()
|
|
1766
|
+
});
|
|
1767
|
+
var HasTierFilterSchema = zod.z.object({
|
|
1768
|
+
hasTier: zod.z.boolean()
|
|
1769
|
+
});
|
|
1770
|
+
var BeeperEconomicsFilterSchema = zod.z.object({
|
|
1771
|
+
maxAttentionPriceUsd: zod.z.number().min(0).optional(),
|
|
1772
|
+
minAttentionPriceUsd: zod.z.number().min(0).optional(),
|
|
1773
|
+
minFid: zod.z.number().int().min(1).optional(),
|
|
1774
|
+
maxFid: zod.z.number().int().min(1).optional(),
|
|
1775
|
+
minBatteryPercentage: zod.z.number().min(0).max(100).optional(),
|
|
1776
|
+
excludePingedToday: zod.z.boolean().optional(),
|
|
1777
|
+
hasRechargedInLastDays: zod.z.number().int().min(1).optional(),
|
|
1778
|
+
activeInLastDays: zod.z.number().int().min(0).optional(),
|
|
1779
|
+
requireLotteryOptIn: zod.z.boolean().optional(),
|
|
1780
|
+
requireQuizOptIn: zod.z.boolean().optional(),
|
|
1781
|
+
minCastCount: zod.z.number().int().min(0).optional(),
|
|
1782
|
+
minClickThroughRate: zod.z.number().min(0).max(1).optional(),
|
|
1783
|
+
isWaitlisted: zod.z.boolean().optional(),
|
|
1784
|
+
hasTier: zod.z.boolean().optional()
|
|
1785
|
+
});
|
|
1786
|
+
var TokenHolderFilterSchema = zod.z.object({
|
|
1787
|
+
tokenHolder: zod.z.object({
|
|
1788
|
+
chain: zod.z.enum(["base", "ethereum"]),
|
|
1789
|
+
contractAddress: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid contract address"),
|
|
1790
|
+
tokenStandard: zod.z.enum(["ERC20", "ERC721", "ERC1155"]),
|
|
1791
|
+
tokenId: zod.z.string().optional(),
|
|
1792
|
+
// Required for ERC1155, optional for ERC721
|
|
1793
|
+
minBalance: zod.z.string().optional(),
|
|
1794
|
+
symbol: zod.z.string().optional(),
|
|
1795
|
+
name: zod.z.string().optional()
|
|
1796
|
+
})
|
|
1797
|
+
});
|
|
1798
|
+
var CachedTokenHolderFilterSchema = zod.z.object({
|
|
1799
|
+
cachedTokenHolder: zod.z.object({
|
|
1800
|
+
chain: zod.z.enum(["base", "ethereum"]),
|
|
1801
|
+
contractAddress: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid contract address"),
|
|
1802
|
+
tokenStandard: zod.z.enum(["ERC20", "ERC721", "ERC1155"]).default("ERC20"),
|
|
1803
|
+
tokenId: zod.z.string().optional(),
|
|
1804
|
+
tokenSymbol: zod.z.string().optional(),
|
|
1805
|
+
tokenName: zod.z.string().optional(),
|
|
1806
|
+
minBalance: zod.z.union([zod.z.string(), zod.z.number()]).optional()
|
|
1807
|
+
})
|
|
1808
|
+
});
|
|
1809
|
+
var HasBaseWalletFilterSchema = zod.z.object({
|
|
1810
|
+
hasBaseWallet: zod.z.boolean()
|
|
1811
|
+
});
|
|
1812
|
+
var HasVerifiedWalletFilterSchema = zod.z.object({
|
|
1813
|
+
hasVerifiedWallet: zod.z.boolean()
|
|
1814
|
+
});
|
|
1815
|
+
var TokenHolderDiscoverySchema = zod.z.object({
|
|
1816
|
+
chain: zod.z.enum(["base", "ethereum"]),
|
|
1817
|
+
contractAddress: zod.z.string(),
|
|
1818
|
+
tokenStandard: zod.z.enum(["ERC20", "ERC721", "ERC1155"]),
|
|
1819
|
+
tokenId: zod.z.string().optional(),
|
|
1820
|
+
minBalance: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
|
|
1821
|
+
symbol: zod.z.string().optional(),
|
|
1822
|
+
name: zod.z.string().optional()
|
|
1823
|
+
});
|
|
1824
|
+
var CachedTokenHolderSchema = zod.z.object({
|
|
1825
|
+
chain: zod.z.enum(["base", "ethereum"]),
|
|
1826
|
+
contractAddress: zod.z.string(),
|
|
1827
|
+
tokenStandard: zod.z.enum(["ERC20", "ERC721", "ERC1155"]).default("ERC20"),
|
|
1828
|
+
tokenId: zod.z.string().optional(),
|
|
1829
|
+
tokenSymbol: zod.z.string().optional(),
|
|
1830
|
+
tokenName: zod.z.string().optional(),
|
|
1831
|
+
minBalance: zod.z.union([zod.z.string(), zod.z.number()]).optional()
|
|
1832
|
+
});
|
|
1833
|
+
var OnchainFilterSchema = zod.z.object({
|
|
1834
|
+
hasBaseWallet: zod.z.boolean().optional(),
|
|
1835
|
+
tokenHolders: zod.z.array(TokenHolderDiscoverySchema).optional(),
|
|
1836
|
+
cachedTokenHolders: zod.z.array(CachedTokenHolderSchema).optional(),
|
|
1837
|
+
hasVerifiedWallet: zod.z.boolean().optional()
|
|
1838
|
+
});
|
|
1839
|
+
var OrderBySchema = zod.z.enum([
|
|
1840
|
+
"attention_price_asc",
|
|
1841
|
+
"attention_price_desc",
|
|
1842
|
+
"neynar_score_desc",
|
|
1843
|
+
"followers_desc",
|
|
1844
|
+
"followers_asc",
|
|
1845
|
+
"recent_activity",
|
|
1846
|
+
"battery_desc",
|
|
1847
|
+
"random"
|
|
1848
|
+
]);
|
|
1849
|
+
var RecipientFilterSchema = zod.z.object({
|
|
1850
|
+
// Platform filter
|
|
1851
|
+
platform: zod.z.enum(["all", "farcaster", "twitter"]).optional(),
|
|
1852
|
+
// Social targeting
|
|
1853
|
+
social: SocialFilterSchema.optional(),
|
|
1854
|
+
// Reputation filters
|
|
1855
|
+
reputation: ReputationFilterSchema.optional(),
|
|
1856
|
+
// Onchain filters
|
|
1857
|
+
onchain: OnchainFilterSchema.optional(),
|
|
1858
|
+
// Beeper economics filters
|
|
1859
|
+
beeperEconomics: BeeperEconomicsFilterSchema.optional(),
|
|
1860
|
+
// Sorting and limits
|
|
1861
|
+
orderBy: OrderBySchema.optional(),
|
|
1862
|
+
limit: zod.z.number().int().min(1).optional()
|
|
1863
|
+
});
|
|
1864
|
+
var FilterExpressionSchema = zod.z.lazy(
|
|
1865
|
+
() => zod.z.union([
|
|
1866
|
+
// Match all users
|
|
1867
|
+
zod.z.object({ all: zod.z.literal(true) }),
|
|
1868
|
+
// Logical AND
|
|
1869
|
+
zod.z.object({ and: zod.z.array(FilterExpressionSchema).min(1) }),
|
|
1870
|
+
// Logical OR
|
|
1871
|
+
zod.z.object({ or: zod.z.array(FilterExpressionSchema).min(1) }),
|
|
1872
|
+
// Logical NOT
|
|
1873
|
+
zod.z.object({ not: FilterExpressionSchema }),
|
|
1874
|
+
// Platform filter
|
|
1875
|
+
PlatformFilterSchema,
|
|
1876
|
+
// Activity filter
|
|
1877
|
+
ActiveInLastDaysFilterSchema,
|
|
1878
|
+
// Reputation filters
|
|
1879
|
+
NeynarScoreMinFilterSchema,
|
|
1880
|
+
NeynarScoreMaxFilterSchema,
|
|
1881
|
+
QuotientScoreMinFilterSchema,
|
|
1882
|
+
QuotientScoreMaxFilterSchema,
|
|
1883
|
+
SpamLabelFilterSchema,
|
|
1884
|
+
ProSubscriptionFilterSchema,
|
|
1885
|
+
MinProTenureDaysFilterSchema,
|
|
1886
|
+
MinTenureDaysFilterSchema,
|
|
1887
|
+
VerifiedOnlyFilterSchema,
|
|
1888
|
+
// Social filters
|
|
1889
|
+
MinFollowersFilterSchema,
|
|
1890
|
+
MaxFollowersFilterSchema,
|
|
1891
|
+
MinFollowingFilterSchema,
|
|
1892
|
+
MaxFollowingFilterSchema,
|
|
1893
|
+
FollowersOfFilterSchema,
|
|
1894
|
+
FollowingOfFilterSchema,
|
|
1895
|
+
MutualsWithFilterSchema,
|
|
1896
|
+
// Economics filters
|
|
1897
|
+
MaxAttentionPriceFilterSchema,
|
|
1898
|
+
MinAttentionPriceFilterSchema,
|
|
1899
|
+
MinFidFilterSchema,
|
|
1900
|
+
MaxFidFilterSchema,
|
|
1901
|
+
MinBatteryPercentageFilterSchema,
|
|
1902
|
+
ExcludePingedTodayFilterSchema,
|
|
1903
|
+
HasRechargedInLastDaysFilterSchema,
|
|
1904
|
+
RequireLotteryOptInFilterSchema,
|
|
1905
|
+
RequireQuizOptInFilterSchema,
|
|
1906
|
+
MinCastCountFilterSchema,
|
|
1907
|
+
MinClickThroughRateFilterSchema,
|
|
1908
|
+
IsWaitlistedFilterSchema,
|
|
1909
|
+
HasTierFilterSchema,
|
|
1910
|
+
// Onchain filters
|
|
1911
|
+
TokenHolderFilterSchema,
|
|
1912
|
+
CachedTokenHolderFilterSchema,
|
|
1913
|
+
HasBaseWalletFilterSchema,
|
|
1914
|
+
HasVerifiedWalletFilterSchema,
|
|
1915
|
+
// Full recipient filter (structured DSL)
|
|
1916
|
+
RecipientFilterSchema,
|
|
1917
|
+
// Legacy field comparison (for backward compatibility)
|
|
1918
|
+
LegacyFieldComparisonSchema
|
|
1919
|
+
])
|
|
1920
|
+
);
|
|
1921
|
+
var RecipientFilterDSLSchema = FilterExpressionSchema;
|
|
1922
|
+
function validateFilter(filter) {
|
|
1923
|
+
const result = FilterExpressionSchema.safeParse(filter);
|
|
1924
|
+
return result.success;
|
|
1925
|
+
}
|
|
1926
|
+
function parseFilter(filter) {
|
|
1927
|
+
return FilterExpressionSchema.parse(filter);
|
|
1928
|
+
}
|
|
1929
|
+
function safeParseFilter(filter) {
|
|
1930
|
+
return FilterExpressionSchema.safeParse(filter);
|
|
1931
|
+
}
|
|
1932
|
+
function validateRecipientFilter(filter) {
|
|
1933
|
+
const result = RecipientFilterSchema.safeParse(filter);
|
|
1934
|
+
return result.success;
|
|
1935
|
+
}
|
|
1936
|
+
function parseRecipientFilter(filter) {
|
|
1937
|
+
return RecipientFilterSchema.parse(filter);
|
|
1938
|
+
}
|
|
1939
|
+
function safeParseRecipientFilter(filter) {
|
|
1940
|
+
return RecipientFilterSchema.safeParse(filter);
|
|
1941
|
+
}
|
|
1942
|
+
function validateFilterHasTargeting(filter) {
|
|
1943
|
+
const hasSocial = filter.social?.specificFids?.length || filter.social?.specificUsernames?.length || filter.social?.followersOf || filter.social?.followingOf || filter.social?.mutualsWith;
|
|
1944
|
+
if (!hasSocial) {
|
|
1945
|
+
const hasOtherFilters = filter.reputation || filter.onchain || filter.beeperEconomics;
|
|
1946
|
+
return !!hasOtherFilters;
|
|
1947
|
+
}
|
|
1948
|
+
return true;
|
|
1949
|
+
}
|
|
1950
|
+
function describeFilters(filter) {
|
|
1951
|
+
const descriptions = [];
|
|
1952
|
+
if (filter.platform && filter.platform !== "all") {
|
|
1953
|
+
descriptions.push(`Platform: ${filter.platform}`);
|
|
1954
|
+
}
|
|
1955
|
+
if (filter.social?.specificFids?.length) {
|
|
1956
|
+
descriptions.push(`${filter.social.specificFids.length} specific users`);
|
|
1957
|
+
}
|
|
1958
|
+
if (filter.social?.followersOf) {
|
|
1959
|
+
descriptions.push(`Followers of FID ${filter.social.followersOf}`);
|
|
1960
|
+
}
|
|
1961
|
+
if (filter.social?.mutualsWith) {
|
|
1962
|
+
descriptions.push(`Mutual followers with FID ${filter.social.mutualsWith}`);
|
|
1963
|
+
}
|
|
1964
|
+
if (filter.social?.minFollowers) {
|
|
1965
|
+
descriptions.push(`Min ${filter.social.minFollowers} followers`);
|
|
1966
|
+
}
|
|
1967
|
+
if (filter.social?.signalTokens?.length) {
|
|
1968
|
+
descriptions.push(
|
|
1969
|
+
`Signal token holders: ${filter.social.signalTokens.length} token(s)`
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
if (filter.social?.timezones && filter.social.timezones.length > 0) {
|
|
1973
|
+
descriptions.push(`${filter.social.timezones.length} timezone(s)`);
|
|
1974
|
+
}
|
|
1975
|
+
if (filter.social?.countries && filter.social.countries.length > 0) {
|
|
1976
|
+
descriptions.push(`${filter.social.countries.length} country/countries`);
|
|
1977
|
+
}
|
|
1978
|
+
if (filter.social?.roles && filter.social.roles.length > 0) {
|
|
1979
|
+
descriptions.push(`${filter.social.roles.length} role(s)`);
|
|
1980
|
+
}
|
|
1981
|
+
if (filter.reputation?.neynarScoreMin) {
|
|
1982
|
+
descriptions.push(`Neynar score >= ${filter.reputation.neynarScoreMin}`);
|
|
1983
|
+
}
|
|
1984
|
+
if (filter.reputation?.quotientScoreMin) {
|
|
1985
|
+
descriptions.push(`Quotient score >= ${filter.reputation.quotientScoreMin}`);
|
|
1986
|
+
}
|
|
1987
|
+
if (filter.reputation?.spamLabel === "not_spam_only") {
|
|
1988
|
+
descriptions.push("Non-spam only");
|
|
1989
|
+
}
|
|
1990
|
+
if (filter.reputation?.proSubscriptionRequired) {
|
|
1991
|
+
descriptions.push("Pro subscribers");
|
|
1992
|
+
}
|
|
1993
|
+
if (filter.onchain?.hasBaseWallet) {
|
|
1994
|
+
descriptions.push("Base wallet holders");
|
|
1995
|
+
}
|
|
1996
|
+
if (filter.onchain?.tokenHolders?.length) {
|
|
1997
|
+
descriptions.push(
|
|
1998
|
+
`Token holders: ${filter.onchain.tokenHolders.length} token(s)`
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
if (filter.onchain?.cachedTokenHolders?.length) {
|
|
2002
|
+
const tokenNames = filter.onchain.cachedTokenHolders.map((t) => t.tokenSymbol || t.contractAddress.slice(0, 10)).join(", ");
|
|
2003
|
+
descriptions.push(`Holders of: ${tokenNames}`);
|
|
2004
|
+
}
|
|
2005
|
+
if (filter.beeperEconomics?.maxAttentionPriceUsd) {
|
|
2006
|
+
descriptions.push(
|
|
2007
|
+
`Max $${filter.beeperEconomics.maxAttentionPriceUsd}/msg`
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
if (filter.beeperEconomics?.activeInLastDays) {
|
|
2011
|
+
descriptions.push(
|
|
2012
|
+
`Active in last ${filter.beeperEconomics.activeInLastDays} days`
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
if (filter.beeperEconomics?.hasRechargedInLastDays) {
|
|
2016
|
+
descriptions.push(
|
|
2017
|
+
`Recharged in last ${filter.beeperEconomics.hasRechargedInLastDays} days`
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
if (filter.beeperEconomics?.minBatteryPercentage) {
|
|
2021
|
+
descriptions.push(`Min ${filter.beeperEconomics.minBatteryPercentage}% battery`);
|
|
2022
|
+
}
|
|
2023
|
+
return descriptions;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/schemas/draft.schema.ts
|
|
2027
|
+
var NetworkSchema = zod.z.enum([
|
|
2028
|
+
"ethereum",
|
|
2029
|
+
"polygon",
|
|
2030
|
+
"base",
|
|
2031
|
+
"arbitrum",
|
|
2032
|
+
"optimism"
|
|
2033
|
+
]);
|
|
2034
|
+
var TokenTypeSchema = zod.z.enum(["USDC", "USDT", "ETH", "MATIC"]);
|
|
2035
|
+
var DistributionStrategySchema = zod.z.enum([
|
|
2036
|
+
"equal",
|
|
2037
|
+
"weighted",
|
|
2038
|
+
"proportional"
|
|
2039
|
+
]);
|
|
2040
|
+
var DraftStatusSchema = zod.z.enum(["draft", "quoted", "executed"]);
|
|
2041
|
+
var DraftInputSchema = zod.z.object({
|
|
2042
|
+
name: zod.z.string().min(1).max(255),
|
|
2043
|
+
amount: zod.z.string().regex(/^\d+(\.\d+)?$/, "Amount must be a valid decimal string"),
|
|
2044
|
+
token: TokenTypeSchema,
|
|
2045
|
+
network: NetworkSchema,
|
|
2046
|
+
filter: RecipientFilterDSLSchema,
|
|
2047
|
+
strategy: DistributionStrategySchema,
|
|
2048
|
+
weights: zod.z.record(zod.z.string(), zod.z.number().min(0)).optional(),
|
|
2049
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
2050
|
+
});
|
|
2051
|
+
var DraftSchema = zod.z.object({
|
|
2052
|
+
id: zod.z.string().uuid(),
|
|
2053
|
+
name: zod.z.string(),
|
|
2054
|
+
amount: zod.z.string(),
|
|
2055
|
+
token: TokenTypeSchema,
|
|
2056
|
+
network: NetworkSchema,
|
|
2057
|
+
filter: RecipientFilterDSLSchema,
|
|
2058
|
+
strategy: DistributionStrategySchema,
|
|
2059
|
+
weights: zod.z.record(zod.z.string(), zod.z.number()).optional(),
|
|
2060
|
+
metadata: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
2061
|
+
createdAt: zod.z.string().datetime(),
|
|
2062
|
+
updatedAt: zod.z.string().datetime(),
|
|
2063
|
+
status: DraftStatusSchema
|
|
2064
|
+
});
|
|
2065
|
+
var DraftUpdateSchema = DraftInputSchema.partial();
|
|
2066
|
+
function validateDraftInput(input) {
|
|
2067
|
+
return DraftInputSchema.safeParse(input);
|
|
2068
|
+
}
|
|
2069
|
+
function parseDraftInput(input) {
|
|
2070
|
+
return DraftInputSchema.parse(input);
|
|
2071
|
+
}
|
|
2072
|
+
function validateDraft(draft) {
|
|
2073
|
+
return DraftSchema.safeParse(draft);
|
|
2074
|
+
}
|
|
2075
|
+
function parseDraft(draft) {
|
|
2076
|
+
return DraftSchema.parse(draft);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/send/draft.ts
|
|
2080
|
+
function createDraft(input) {
|
|
2081
|
+
const validated = validateDraftInput2(input);
|
|
2082
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2083
|
+
const draft = {
|
|
2084
|
+
id: crypto.randomUUID(),
|
|
2085
|
+
name: validated.name,
|
|
2086
|
+
amount: validated.amount,
|
|
2087
|
+
token: validated.token,
|
|
2088
|
+
network: validated.network,
|
|
2089
|
+
filter: validated.filter,
|
|
2090
|
+
strategy: validated.strategy,
|
|
2091
|
+
createdAt: now,
|
|
2092
|
+
updatedAt: now,
|
|
2093
|
+
status: "draft"
|
|
2094
|
+
};
|
|
2095
|
+
if (validated.weights !== void 0) {
|
|
2096
|
+
draft.weights = validated.weights;
|
|
2097
|
+
}
|
|
2098
|
+
if (validated.metadata !== void 0) {
|
|
2099
|
+
draft.metadata = validated.metadata;
|
|
2100
|
+
}
|
|
2101
|
+
return draft;
|
|
2102
|
+
}
|
|
2103
|
+
function validateDraftInput2(input) {
|
|
2104
|
+
const result = DraftInputSchema.safeParse(input);
|
|
2105
|
+
if (!result.success) {
|
|
2106
|
+
const issues = result.error.issues.map((issue) => ({
|
|
2107
|
+
path: issue.path.join("."),
|
|
2108
|
+
message: issue.message
|
|
2109
|
+
}));
|
|
2110
|
+
throw new BeeperError({
|
|
2111
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2112
|
+
message: "Invalid draft input",
|
|
2113
|
+
context: {
|
|
2114
|
+
details: { issues }
|
|
2115
|
+
},
|
|
2116
|
+
retryable: false
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
return result.data;
|
|
2120
|
+
}
|
|
2121
|
+
function updateDraft(draft, updates) {
|
|
2122
|
+
const mergedInput = {
|
|
2123
|
+
name: updates.name ?? draft.name,
|
|
2124
|
+
amount: updates.amount ?? draft.amount,
|
|
2125
|
+
token: updates.token ?? draft.token,
|
|
2126
|
+
network: updates.network ?? draft.network,
|
|
2127
|
+
filter: updates.filter ?? draft.filter,
|
|
2128
|
+
strategy: updates.strategy ?? draft.strategy
|
|
2129
|
+
};
|
|
2130
|
+
if (updates.weights !== void 0) {
|
|
2131
|
+
mergedInput.weights = updates.weights;
|
|
2132
|
+
} else if (draft.weights !== void 0) {
|
|
2133
|
+
mergedInput.weights = draft.weights;
|
|
2134
|
+
}
|
|
2135
|
+
if (updates.metadata !== void 0) {
|
|
2136
|
+
mergedInput.metadata = updates.metadata;
|
|
2137
|
+
} else if (draft.metadata !== void 0) {
|
|
2138
|
+
mergedInput.metadata = draft.metadata;
|
|
2139
|
+
}
|
|
2140
|
+
const validated = validateDraftInput2(mergedInput);
|
|
2141
|
+
const updatedDraft = {
|
|
2142
|
+
...draft,
|
|
2143
|
+
name: validated.name,
|
|
2144
|
+
amount: validated.amount,
|
|
2145
|
+
token: validated.token,
|
|
2146
|
+
network: validated.network,
|
|
2147
|
+
filter: validated.filter,
|
|
2148
|
+
strategy: validated.strategy,
|
|
2149
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2150
|
+
};
|
|
2151
|
+
if (validated.weights !== void 0) {
|
|
2152
|
+
updatedDraft.weights = validated.weights;
|
|
2153
|
+
}
|
|
2154
|
+
if (validated.metadata !== void 0) {
|
|
2155
|
+
updatedDraft.metadata = validated.metadata;
|
|
2156
|
+
}
|
|
2157
|
+
return updatedDraft;
|
|
2158
|
+
}
|
|
2159
|
+
function isReadyForQuote(draft) {
|
|
2160
|
+
return draft.status === "draft" && draft.name.length > 0 && parseFloat(draft.amount) > 0;
|
|
2161
|
+
}
|
|
2162
|
+
var ApiQuoteResponseSchema = zod.z.object({
|
|
2163
|
+
id: zod.z.string().regex(/^qt_[a-zA-Z0-9]+$/),
|
|
2164
|
+
status: zod.z.enum([
|
|
2165
|
+
"pending",
|
|
2166
|
+
"deposit_confirmed",
|
|
2167
|
+
"executing",
|
|
2168
|
+
"completed",
|
|
2169
|
+
"expired",
|
|
2170
|
+
"failed"
|
|
2171
|
+
]),
|
|
2172
|
+
recipientCount: zod.z.number().int().min(0),
|
|
2173
|
+
totalAmount: zod.z.string(),
|
|
2174
|
+
protocolFee: zod.z.string(),
|
|
2175
|
+
depositAmount: zod.z.string(),
|
|
2176
|
+
depositAddress: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
|
|
2177
|
+
depositChainId: zod.z.number().int(),
|
|
2178
|
+
depositTokenAddress: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
|
|
2179
|
+
expiresAt: zod.z.string().datetime(),
|
|
2180
|
+
input: zod.z.record(zod.z.unknown()),
|
|
2181
|
+
createdAt: zod.z.string().datetime(),
|
|
2182
|
+
updatedAt: zod.z.string().datetime()
|
|
2183
|
+
});
|
|
2184
|
+
async function createQuote(httpClient, draft, opts) {
|
|
2185
|
+
const body = {
|
|
2186
|
+
filter: draft.filter,
|
|
2187
|
+
tokenAddress: getTokenAddress(draft.token, draft.network),
|
|
2188
|
+
chainId: getChainId(draft.network),
|
|
2189
|
+
amountPerRecipient: draft.amount,
|
|
2190
|
+
budgetCap: opts?.amount ?? draft.amount,
|
|
2191
|
+
memo: draft.name,
|
|
2192
|
+
metadata: draft.metadata,
|
|
2193
|
+
ttlSeconds: 300
|
|
2194
|
+
};
|
|
2195
|
+
const response = await httpClient.post(
|
|
2196
|
+
"/api/v1/sdk/send/quotes",
|
|
2197
|
+
body,
|
|
2198
|
+
{},
|
|
2199
|
+
ApiQuoteResponseSchema
|
|
2200
|
+
);
|
|
2201
|
+
return response.data;
|
|
2202
|
+
}
|
|
2203
|
+
async function getQuote(httpClient, quoteId) {
|
|
2204
|
+
if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
|
|
2205
|
+
throw new BeeperError({
|
|
2206
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2207
|
+
message: "Invalid quote ID format. Expected format: qt_xxxxx",
|
|
2208
|
+
retryable: false
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
const response = await httpClient.get(
|
|
2212
|
+
`/api/v1/sdk/send/quotes/${quoteId}`,
|
|
2213
|
+
{},
|
|
2214
|
+
ApiQuoteResponseSchema
|
|
2215
|
+
);
|
|
2216
|
+
return response.data;
|
|
2217
|
+
}
|
|
2218
|
+
function isQuoteExpired(quote) {
|
|
2219
|
+
return quote.status === "expired" || new Date(quote.expiresAt) < /* @__PURE__ */ new Date();
|
|
2220
|
+
}
|
|
2221
|
+
function isReadyForDeposit(quote) {
|
|
2222
|
+
return quote.status === "pending" && !isQuoteExpired(quote);
|
|
2223
|
+
}
|
|
2224
|
+
function getChainId(network) {
|
|
2225
|
+
const chainIds = {
|
|
2226
|
+
ethereum: 1,
|
|
2227
|
+
base: 8453,
|
|
2228
|
+
arbitrum: 42161,
|
|
2229
|
+
polygon: 137,
|
|
2230
|
+
optimism: 10
|
|
2231
|
+
};
|
|
2232
|
+
return chainIds[network] ?? 1;
|
|
2233
|
+
}
|
|
2234
|
+
function getTokenAddress(token, network) {
|
|
2235
|
+
const addresses = {
|
|
2236
|
+
USDC: {
|
|
2237
|
+
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
2238
|
+
base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
2239
|
+
arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
2240
|
+
polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
2241
|
+
optimism: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
|
|
2242
|
+
},
|
|
2243
|
+
USDT: {
|
|
2244
|
+
ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
2245
|
+
base: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
2246
|
+
arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
2247
|
+
polygon: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
2248
|
+
optimism: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
return addresses[token]?.[network] ?? "0x0000000000000000000000000000000000000000";
|
|
2252
|
+
}
|
|
2253
|
+
var ConfirmDepositResponseSchema = zod.z.object({
|
|
2254
|
+
quoteId: zod.z.string(),
|
|
2255
|
+
status: zod.z.enum(["confirmed", "pending_verification"]),
|
|
2256
|
+
detectedAmount: zod.z.string(),
|
|
2257
|
+
sufficient: zod.z.boolean(),
|
|
2258
|
+
blockNumber: zod.z.number().int(),
|
|
2259
|
+
confirmedAt: zod.z.string().datetime()
|
|
2260
|
+
});
|
|
2261
|
+
async function confirmDeposit(httpClient, quoteId, params) {
|
|
2262
|
+
validateQuoteId(quoteId);
|
|
2263
|
+
validateTxHash(params.txHash);
|
|
2264
|
+
if (!params.idempotencyKey || params.idempotencyKey.length === 0) {
|
|
2265
|
+
throw new BeeperError({
|
|
2266
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2267
|
+
message: "Idempotency key is required for deposit confirmation",
|
|
2268
|
+
retryable: false
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
const response = await httpClient.post(
|
|
2272
|
+
`/api/v1/sdk/send/quotes/${quoteId}/confirm`,
|
|
2273
|
+
{ txHash: params.txHash },
|
|
2274
|
+
{ idempotencyKey: params.idempotencyKey },
|
|
2275
|
+
ConfirmDepositResponseSchema
|
|
2276
|
+
);
|
|
2277
|
+
return response.data;
|
|
2278
|
+
}
|
|
2279
|
+
function isDepositSufficient(result) {
|
|
2280
|
+
return result.status === "confirmed" && result.sufficient;
|
|
2281
|
+
}
|
|
2282
|
+
function validateTxHash(txHash) {
|
|
2283
|
+
const txHashRegex = /^0x[a-fA-F0-9]{64}$/;
|
|
2284
|
+
if (!txHashRegex.test(txHash)) {
|
|
2285
|
+
throw new BeeperError({
|
|
2286
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2287
|
+
message: "Invalid transaction hash format. Expected 0x followed by 64 hex characters.",
|
|
2288
|
+
context: {
|
|
2289
|
+
details: {
|
|
2290
|
+
provided: txHash,
|
|
2291
|
+
expectedFormat: "0x + 64 hex characters"
|
|
2292
|
+
}
|
|
2293
|
+
},
|
|
2294
|
+
retryable: false
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
function validateQuoteId(quoteId) {
|
|
2299
|
+
if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
|
|
2300
|
+
throw new BeeperError({
|
|
2301
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2302
|
+
message: "Invalid quote ID format. Expected format: qt_xxxxx",
|
|
2303
|
+
retryable: false
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
function generateDepositIdempotencyKey(quoteId) {
|
|
2308
|
+
return `confirm-${quoteId}`;
|
|
2309
|
+
}
|
|
2310
|
+
var ExecuteSendResponseSchema = zod.z.object({
|
|
2311
|
+
quoteId: zod.z.string(),
|
|
2312
|
+
status: zod.z.enum(["executing", "queued"]),
|
|
2313
|
+
estimatedCompletionAt: zod.z.string().datetime(),
|
|
2314
|
+
batchId: zod.z.string()
|
|
2315
|
+
});
|
|
2316
|
+
async function executeSend(httpClient, quoteId, params) {
|
|
2317
|
+
validateQuoteId2(quoteId);
|
|
2318
|
+
if (!params.idempotencyKey || params.idempotencyKey.length === 0) {
|
|
2319
|
+
throw new BeeperError({
|
|
2320
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2321
|
+
message: "Idempotency key is required for execution",
|
|
2322
|
+
retryable: false
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
const response = await httpClient.post(
|
|
2326
|
+
`/api/v1/sdk/send/quotes/${quoteId}/execute`,
|
|
2327
|
+
{},
|
|
2328
|
+
// Empty body as per HTTP contract
|
|
2329
|
+
{ idempotencyKey: params.idempotencyKey },
|
|
2330
|
+
ExecuteSendResponseSchema
|
|
2331
|
+
);
|
|
2332
|
+
return response.data;
|
|
2333
|
+
}
|
|
2334
|
+
function isExecuting(result) {
|
|
2335
|
+
return result.status === "executing" || result.status === "queued";
|
|
2336
|
+
}
|
|
2337
|
+
function getEstimatedTimeRemaining(result) {
|
|
2338
|
+
const estimatedCompletion = new Date(result.estimatedCompletionAt);
|
|
2339
|
+
const now = /* @__PURE__ */ new Date();
|
|
2340
|
+
return Math.max(0, estimatedCompletion.getTime() - now.getTime());
|
|
2341
|
+
}
|
|
2342
|
+
function validateQuoteId2(quoteId) {
|
|
2343
|
+
if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
|
|
2344
|
+
throw new BeeperError({
|
|
2345
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2346
|
+
message: "Invalid quote ID format. Expected format: qt_xxxxx",
|
|
2347
|
+
retryable: false
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
function generateExecuteIdempotencyKey(quoteId) {
|
|
2352
|
+
return `execute-${quoteId}`;
|
|
2353
|
+
}
|
|
2354
|
+
var ReceiptTransactionSchema = zod.z.object({
|
|
2355
|
+
recipient: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
|
|
2356
|
+
amount: zod.z.string(),
|
|
2357
|
+
txHash: zod.z.string().nullable(),
|
|
2358
|
+
status: zod.z.enum(["success", "failed"]),
|
|
2359
|
+
error: zod.z.string().nullable().optional()
|
|
2360
|
+
});
|
|
2361
|
+
var ApiReceiptResponseSchema = zod.z.object({
|
|
2362
|
+
quoteId: zod.z.string(),
|
|
2363
|
+
status: zod.z.enum(["completed", "partial", "failed"]),
|
|
2364
|
+
successCount: zod.z.number().int().min(0),
|
|
2365
|
+
failureCount: zod.z.number().int().min(0),
|
|
2366
|
+
totalSent: zod.z.string(),
|
|
2367
|
+
refundAmount: zod.z.string(),
|
|
2368
|
+
refundTxHash: zod.z.string().nullable().optional(),
|
|
2369
|
+
transactions: zod.z.array(ReceiptTransactionSchema),
|
|
2370
|
+
completedAt: zod.z.string().datetime()
|
|
2371
|
+
});
|
|
2372
|
+
var DEFAULT_MAX_ATTEMPTS = 60;
|
|
2373
|
+
var DEFAULT_INTERVAL_MS = 5e3;
|
|
2374
|
+
async function getReceipt(httpClient, quoteId) {
|
|
2375
|
+
if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
|
|
2376
|
+
throw new BeeperError({
|
|
2377
|
+
code: ErrorCodes.VALIDATION_ERROR,
|
|
2378
|
+
message: "Invalid quote ID format. Expected format: qt_xxxxx",
|
|
2379
|
+
retryable: false
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
const response = await httpClient.get(
|
|
2383
|
+
`/api/v1/sdk/send/quotes/${quoteId}/receipt`,
|
|
2384
|
+
{},
|
|
2385
|
+
ApiReceiptResponseSchema
|
|
2386
|
+
);
|
|
2387
|
+
return response.data;
|
|
2388
|
+
}
|
|
2389
|
+
async function pollUntilComplete(httpClient, quoteId, opts) {
|
|
2390
|
+
const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
2391
|
+
const intervalMs = opts?.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
2392
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2393
|
+
if (opts?.signal?.aborted) {
|
|
2394
|
+
throw new BeeperError({
|
|
2395
|
+
code: ErrorCodes.TIMEOUT,
|
|
2396
|
+
message: "Polling was aborted",
|
|
2397
|
+
retryable: false
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
try {
|
|
2401
|
+
const receipt = await getReceipt(httpClient, quoteId);
|
|
2402
|
+
if (isComplete(receipt)) return receipt;
|
|
2403
|
+
} catch (error) {
|
|
2404
|
+
if (!(error instanceof BeeperError && error.context.statusCode === 428)) {
|
|
2405
|
+
throw error;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
if (attempt < maxAttempts - 1) {
|
|
2409
|
+
await sleep(intervalMs, opts?.signal);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
throw new BeeperError({
|
|
2413
|
+
code: ErrorCodes.QUOTE_EXPIRED,
|
|
2414
|
+
message: `Polling timed out after ${maxAttempts} attempts`,
|
|
2415
|
+
context: { details: { quoteId, maxAttempts } },
|
|
2416
|
+
retryable: false
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
function isComplete(receipt) {
|
|
2420
|
+
return ["completed", "partial", "failed"].includes(receipt.status);
|
|
2421
|
+
}
|
|
2422
|
+
function isSuccess(receipt) {
|
|
2423
|
+
return receipt.status === "completed" && receipt.failureCount === 0;
|
|
2424
|
+
}
|
|
2425
|
+
function getSuccessRate(receipt) {
|
|
2426
|
+
const total = receipt.successCount + receipt.failureCount;
|
|
2427
|
+
return total === 0 ? 0 : receipt.successCount / total * 100;
|
|
2428
|
+
}
|
|
2429
|
+
function getFailedTransactions(receipt) {
|
|
2430
|
+
return receipt.transactions.filter((tx) => tx.status === "failed");
|
|
2431
|
+
}
|
|
2432
|
+
function sleep(ms, signal) {
|
|
2433
|
+
return new Promise((resolve, reject) => {
|
|
2434
|
+
const timeoutId = setTimeout(resolve, ms);
|
|
2435
|
+
signal?.addEventListener("abort", () => {
|
|
2436
|
+
clearTimeout(timeoutId);
|
|
2437
|
+
reject(new Error("Aborted"));
|
|
2438
|
+
});
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// src/client/FilterBuilder.ts
|
|
2443
|
+
var FilterExpression = class {
|
|
2444
|
+
expression;
|
|
2445
|
+
constructor(expression) {
|
|
2446
|
+
this.expression = expression;
|
|
2447
|
+
}
|
|
2448
|
+
/**
|
|
2449
|
+
* Convert the filter expression to JSON format for API requests
|
|
2450
|
+
*/
|
|
2451
|
+
toJSON() {
|
|
2452
|
+
return this.expression;
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Combine this filter with another using AND logic
|
|
2456
|
+
*/
|
|
2457
|
+
and(other) {
|
|
2458
|
+
return FilterBuilder.and([this, other]);
|
|
2459
|
+
}
|
|
2460
|
+
/**
|
|
2461
|
+
* Combine this filter with another using OR logic
|
|
2462
|
+
*/
|
|
2463
|
+
or(other) {
|
|
2464
|
+
return FilterBuilder.or([this, other]);
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* Negate this filter
|
|
2468
|
+
*/
|
|
2469
|
+
not() {
|
|
2470
|
+
return FilterBuilder.not(this);
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
var FilterBuilder = class {
|
|
2474
|
+
// ===========================================================================
|
|
2475
|
+
// Logical Combinators
|
|
2476
|
+
// ===========================================================================
|
|
2477
|
+
/**
|
|
2478
|
+
* Combine multiple filters with AND logic (all must match)
|
|
2479
|
+
* @param filters - Array of filter expressions to combine
|
|
2480
|
+
*/
|
|
2481
|
+
static and(filters) {
|
|
2482
|
+
if (filters.length === 0) {
|
|
2483
|
+
throw new Error("and() requires at least one filter");
|
|
2484
|
+
}
|
|
2485
|
+
if (filters.length === 1) {
|
|
2486
|
+
return filters[0];
|
|
2487
|
+
}
|
|
2488
|
+
return new FilterExpression({
|
|
2489
|
+
and: filters.map((f) => f.toJSON())
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Combine multiple filters with OR logic (any must match)
|
|
2494
|
+
* @param filters - Array of filter expressions to combine
|
|
2495
|
+
*/
|
|
2496
|
+
static or(filters) {
|
|
2497
|
+
if (filters.length === 0) {
|
|
2498
|
+
throw new Error("or() requires at least one filter");
|
|
2499
|
+
}
|
|
2500
|
+
if (filters.length === 1) {
|
|
2501
|
+
return filters[0];
|
|
2502
|
+
}
|
|
2503
|
+
return new FilterExpression({
|
|
2504
|
+
or: filters.map((f) => f.toJSON())
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Negate a filter (matches users who do NOT match the filter)
|
|
2509
|
+
* @param filter - Filter expression to negate
|
|
2510
|
+
*/
|
|
2511
|
+
static not(filter) {
|
|
2512
|
+
return new FilterExpression({
|
|
2513
|
+
not: filter.toJSON()
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
// ===========================================================================
|
|
2517
|
+
// Platform Filter
|
|
2518
|
+
// ===========================================================================
|
|
2519
|
+
/**
|
|
2520
|
+
* Filter by platform
|
|
2521
|
+
* @param p - Platform to target: 'all', 'farcaster', or 'twitter'
|
|
2522
|
+
*/
|
|
2523
|
+
static platform(p) {
|
|
2524
|
+
return new FilterExpression({ platform: p });
|
|
2525
|
+
}
|
|
2526
|
+
// ===========================================================================
|
|
2527
|
+
// Activity Filters
|
|
2528
|
+
// ===========================================================================
|
|
2529
|
+
/**
|
|
2530
|
+
* Filter users who have been active within the last N days
|
|
2531
|
+
* @param days - Number of days (1-365)
|
|
2532
|
+
*/
|
|
2533
|
+
static activeInLastDays(days) {
|
|
2534
|
+
if (days < 1 || days > 365) {
|
|
2535
|
+
throw new Error("activeInLastDays must be between 1 and 365");
|
|
2536
|
+
}
|
|
2537
|
+
return new FilterExpression({ activeInLastDays: days });
|
|
2538
|
+
}
|
|
2539
|
+
// ===========================================================================
|
|
2540
|
+
// Reputation Filters (Neynar Score)
|
|
2541
|
+
// ===========================================================================
|
|
2542
|
+
/**
|
|
2543
|
+
* Filter users with Neynar score >= minimum
|
|
2544
|
+
* @param score - Minimum score (0-1)
|
|
2545
|
+
*/
|
|
2546
|
+
static neynarScoreMin(score) {
|
|
2547
|
+
if (score < 0 || score > 1) {
|
|
2548
|
+
throw new Error("neynarScoreMin must be between 0 and 1");
|
|
2549
|
+
}
|
|
2550
|
+
return new FilterExpression({ neynarScoreMin: score });
|
|
2551
|
+
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Filter users with Neynar score <= maximum
|
|
2554
|
+
* @param score - Maximum score (0-1)
|
|
2555
|
+
*/
|
|
2556
|
+
static neynarScoreMax(score) {
|
|
2557
|
+
if (score < 0 || score > 1) {
|
|
2558
|
+
throw new Error("neynarScoreMax must be between 0 and 1");
|
|
2559
|
+
}
|
|
2560
|
+
return new FilterExpression({ neynarScoreMax: score });
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Filter users with Neynar score in a range
|
|
2564
|
+
* @param min - Minimum score (0-1, optional)
|
|
2565
|
+
* @param max - Maximum score (0-1, optional)
|
|
2566
|
+
*/
|
|
2567
|
+
static neynarScoreRange(min, max) {
|
|
2568
|
+
if (min !== void 0 && (min < 0 || min > 1)) {
|
|
2569
|
+
throw new Error("neynarScoreRange min must be between 0 and 1");
|
|
2570
|
+
}
|
|
2571
|
+
if (max !== void 0 && (max < 0 || max > 1)) {
|
|
2572
|
+
throw new Error("neynarScoreRange max must be between 0 and 1");
|
|
2573
|
+
}
|
|
2574
|
+
const range = {};
|
|
2575
|
+
if (min !== void 0) range.min = min;
|
|
2576
|
+
if (max !== void 0) range.max = max;
|
|
2577
|
+
return new FilterExpression({ neynarScoreRange: range });
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Filter by spam label
|
|
2581
|
+
* @param label - Spam label filter: 'not_spam_only', 'spam_only', or 'all'
|
|
2582
|
+
*/
|
|
2583
|
+
static spamLabel(label) {
|
|
2584
|
+
return new FilterExpression({ spamLabel: label });
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Filter to exclude spam users (convenience method)
|
|
2588
|
+
* @deprecated Use spamLabel('not_spam_only') instead
|
|
2589
|
+
*/
|
|
2590
|
+
static excludeSpam() {
|
|
2591
|
+
return new FilterExpression({ spamLabel: "not_spam_only" });
|
|
2592
|
+
}
|
|
2593
|
+
// ===========================================================================
|
|
2594
|
+
// Social Graph Filters (Followers)
|
|
2595
|
+
// ===========================================================================
|
|
2596
|
+
/**
|
|
2597
|
+
* Filter users with at least N followers
|
|
2598
|
+
* @param count - Minimum follower count
|
|
2599
|
+
*/
|
|
2600
|
+
static minFollowers(count) {
|
|
2601
|
+
if (count < 0) {
|
|
2602
|
+
throw new Error("minFollowers must be >= 0");
|
|
2603
|
+
}
|
|
2604
|
+
return new FilterExpression({ minFollowers: count });
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Filter users with at most N followers
|
|
2608
|
+
* @param count - Maximum follower count
|
|
2609
|
+
*/
|
|
2610
|
+
static maxFollowers(count) {
|
|
2611
|
+
if (count < 0) {
|
|
2612
|
+
throw new Error("maxFollowers must be >= 0");
|
|
2613
|
+
}
|
|
2614
|
+
return new FilterExpression({ maxFollowers: count });
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Filter users with follower count in a range
|
|
2618
|
+
* @param min - Minimum follower count (optional)
|
|
2619
|
+
* @param max - Maximum follower count (optional)
|
|
2620
|
+
*/
|
|
2621
|
+
static followerRange(min, max) {
|
|
2622
|
+
if (min !== void 0 && min < 0) {
|
|
2623
|
+
throw new Error("followerRange min must be >= 0");
|
|
2624
|
+
}
|
|
2625
|
+
if (max !== void 0 && max < 0) {
|
|
2626
|
+
throw new Error("followerRange max must be >= 0");
|
|
2627
|
+
}
|
|
2628
|
+
const range = {};
|
|
2629
|
+
if (min !== void 0) range.min = min;
|
|
2630
|
+
if (max !== void 0) range.max = max;
|
|
2631
|
+
return new FilterExpression({ followerRange: range });
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Filter users who follow a specific FID
|
|
2635
|
+
* @param fid - Farcaster ID to check followers of
|
|
2636
|
+
*/
|
|
2637
|
+
static followersOf(fid) {
|
|
2638
|
+
if (fid < 1) {
|
|
2639
|
+
throw new Error("followersOf FID must be >= 1");
|
|
2640
|
+
}
|
|
2641
|
+
return new FilterExpression({ followersOf: fid });
|
|
2642
|
+
}
|
|
2643
|
+
/**
|
|
2644
|
+
* Filter users who have mutual follows with a specific FID
|
|
2645
|
+
* @param fid - Farcaster ID to check mutual follows with
|
|
2646
|
+
*/
|
|
2647
|
+
static mutualsWith(fid) {
|
|
2648
|
+
if (fid < 1) {
|
|
2649
|
+
throw new Error("mutualsWith FID must be >= 1");
|
|
2650
|
+
}
|
|
2651
|
+
return new FilterExpression({ mutualsWith: fid });
|
|
2652
|
+
}
|
|
2653
|
+
// ===========================================================================
|
|
2654
|
+
// Economics Filters
|
|
2655
|
+
// ===========================================================================
|
|
2656
|
+
/**
|
|
2657
|
+
* Filter users with attention price <= maximum USD
|
|
2658
|
+
* @param usd - Maximum attention price in USD
|
|
2659
|
+
*/
|
|
2660
|
+
static maxAttentionPriceUsd(usd) {
|
|
2661
|
+
if (usd < 0) {
|
|
2662
|
+
throw new Error("maxAttentionPriceUsd must be >= 0");
|
|
2663
|
+
}
|
|
2664
|
+
return new FilterExpression({ maxAttentionPriceUsd: usd });
|
|
2665
|
+
}
|
|
2666
|
+
// ===========================================================================
|
|
2667
|
+
// Onchain Filters (Token Holdings)
|
|
2668
|
+
// ===========================================================================
|
|
2669
|
+
/**
|
|
2670
|
+
* Filter users who hold a specific token
|
|
2671
|
+
* @param opts - Token holder options (tokenAddress, chainId, optional minBalance)
|
|
2672
|
+
*/
|
|
2673
|
+
static tokenHolder(opts) {
|
|
2674
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(opts.tokenAddress)) {
|
|
2675
|
+
throw new Error("tokenAddress must be a valid Ethereum address");
|
|
2676
|
+
}
|
|
2677
|
+
if (opts.minBalance !== void 0) {
|
|
2678
|
+
if (!/^[0-9]+$/.test(opts.minBalance)) {
|
|
2679
|
+
throw new Error("minBalance must be a numeric string (wei)");
|
|
2680
|
+
}
|
|
2681
|
+
return new FilterExpression({
|
|
2682
|
+
walletMinBalance: {
|
|
2683
|
+
tokenAddress: opts.tokenAddress,
|
|
2684
|
+
chainId: opts.chainId,
|
|
2685
|
+
minBalance: opts.minBalance
|
|
2686
|
+
}
|
|
2687
|
+
});
|
|
2688
|
+
}
|
|
2689
|
+
return new FilterExpression({
|
|
2690
|
+
walletHasToken: {
|
|
2691
|
+
tokenAddress: opts.tokenAddress,
|
|
2692
|
+
chainId: opts.chainId
|
|
2693
|
+
}
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Filter users who hold any of the specified tokens (multiple token filter)
|
|
2698
|
+
* @param opts - Array of token holder options (max 10)
|
|
2699
|
+
*/
|
|
2700
|
+
static tokenHolders(opts) {
|
|
2701
|
+
if (opts.length === 0) {
|
|
2702
|
+
throw new Error("tokenHolders requires at least one token option");
|
|
2703
|
+
}
|
|
2704
|
+
if (opts.length > 10) {
|
|
2705
|
+
throw new Error("tokenHolders cannot exceed 10 tokens");
|
|
2706
|
+
}
|
|
2707
|
+
for (const opt of opts) {
|
|
2708
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(opt.tokenAddress)) {
|
|
2709
|
+
throw new Error("tokenAddress must be a valid Ethereum address");
|
|
2710
|
+
}
|
|
2711
|
+
if (opt.minBalance !== void 0 && !/^[0-9]+$/.test(opt.minBalance)) {
|
|
2712
|
+
throw new Error("minBalance must be a numeric string (wei)");
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return new FilterExpression({
|
|
2716
|
+
tokenHolders: opts.map((o) => ({
|
|
2717
|
+
tokenAddress: o.tokenAddress,
|
|
2718
|
+
chainId: o.chainId,
|
|
2719
|
+
...o.minBalance !== void 0 ? { minBalance: o.minBalance } : {}
|
|
2720
|
+
}))
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Filter users who hold any of the specified tokens (uses pre-synced cache for faster queries)
|
|
2725
|
+
* @param opts - Array of token holder options (max 10)
|
|
2726
|
+
*/
|
|
2727
|
+
static cachedTokenHolders(opts) {
|
|
2728
|
+
if (opts.length === 0) {
|
|
2729
|
+
throw new Error("cachedTokenHolders requires at least one token option");
|
|
2730
|
+
}
|
|
2731
|
+
if (opts.length > 10) {
|
|
2732
|
+
throw new Error("cachedTokenHolders cannot exceed 10 tokens");
|
|
2733
|
+
}
|
|
2734
|
+
for (const opt of opts) {
|
|
2735
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(opt.tokenAddress)) {
|
|
2736
|
+
throw new Error("tokenAddress must be a valid Ethereum address");
|
|
2737
|
+
}
|
|
2738
|
+
if (opt.minBalance !== void 0 && !/^[0-9]+$/.test(opt.minBalance)) {
|
|
2739
|
+
throw new Error("minBalance must be a numeric string (wei)");
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
return new FilterExpression({
|
|
2743
|
+
cachedTokenHolders: opts.map((o) => ({
|
|
2744
|
+
tokenAddress: o.tokenAddress,
|
|
2745
|
+
chainId: o.chainId,
|
|
2746
|
+
...o.minBalance !== void 0 ? { minBalance: o.minBalance } : {}
|
|
2747
|
+
}))
|
|
2748
|
+
});
|
|
2749
|
+
}
|
|
2750
|
+
/**
|
|
2751
|
+
* Filter users who have configured specific signal tokens
|
|
2752
|
+
* @param tokenAddresses - Array of token contract addresses (max 20)
|
|
2753
|
+
*/
|
|
2754
|
+
static signalTokens(tokenAddresses) {
|
|
2755
|
+
if (tokenAddresses.length === 0) {
|
|
2756
|
+
throw new Error("signalTokens requires at least one token address");
|
|
2757
|
+
}
|
|
2758
|
+
if (tokenAddresses.length > 20) {
|
|
2759
|
+
throw new Error("signalTokens cannot exceed 20 tokens");
|
|
2760
|
+
}
|
|
2761
|
+
for (const addr of tokenAddresses) {
|
|
2762
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(addr)) {
|
|
2763
|
+
throw new Error("tokenAddress must be a valid Ethereum address");
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
return new FilterExpression({ signalTokens: tokenAddresses });
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Filter users with a wallet on a specific chain
|
|
2770
|
+
* @param chain - Blockchain chain
|
|
2771
|
+
*/
|
|
2772
|
+
static walletChain(chain) {
|
|
2773
|
+
return new FilterExpression({ walletChain: chain });
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* Filter users who have a Base chain wallet
|
|
2777
|
+
*/
|
|
2778
|
+
static hasBaseWallet() {
|
|
2779
|
+
return new FilterExpression({ hasBaseWallet: true });
|
|
2780
|
+
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Filter users who have a verified wallet
|
|
2783
|
+
*/
|
|
2784
|
+
static hasVerifiedWallet() {
|
|
2785
|
+
return new FilterExpression({ hasVerifiedWallet: true });
|
|
2786
|
+
}
|
|
2787
|
+
// ===========================================================================
|
|
2788
|
+
// Verification Filters
|
|
2789
|
+
// ===========================================================================
|
|
2790
|
+
/**
|
|
2791
|
+
* Filter to only verified users
|
|
2792
|
+
*/
|
|
2793
|
+
static verifiedOnly() {
|
|
2794
|
+
return new FilterExpression({ verifiedOnly: true });
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Filter to only users with Pro subscription
|
|
2798
|
+
*/
|
|
2799
|
+
static proSubscriptionRequired() {
|
|
2800
|
+
return new FilterExpression({ proSubscriptionRequired: true });
|
|
2801
|
+
}
|
|
2802
|
+
/**
|
|
2803
|
+
* Filter users with minimum account tenure
|
|
2804
|
+
* @param days - Minimum number of days since account creation
|
|
2805
|
+
*/
|
|
2806
|
+
static minTenureDays(days) {
|
|
2807
|
+
if (days < 0) {
|
|
2808
|
+
throw new Error("minTenureDays must be >= 0");
|
|
2809
|
+
}
|
|
2810
|
+
return new FilterExpression({ minTenureDays: days });
|
|
2811
|
+
}
|
|
2812
|
+
// ===========================================================================
|
|
2813
|
+
// User ID Filters
|
|
2814
|
+
// ===========================================================================
|
|
2815
|
+
/**
|
|
2816
|
+
* Filter by specific user ID
|
|
2817
|
+
* @param id - User ID
|
|
2818
|
+
*/
|
|
2819
|
+
static userId(id) {
|
|
2820
|
+
return new FilterExpression({ userId: id });
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Filter by multiple user IDs
|
|
2824
|
+
* @param ids - Array of user IDs (max 1000)
|
|
2825
|
+
*/
|
|
2826
|
+
static userIds(ids) {
|
|
2827
|
+
if (ids.length === 0) {
|
|
2828
|
+
throw new Error("userIds requires at least one ID");
|
|
2829
|
+
}
|
|
2830
|
+
if (ids.length > 1e3) {
|
|
2831
|
+
throw new Error("userIds cannot exceed 1000 IDs");
|
|
2832
|
+
}
|
|
2833
|
+
return new FilterExpression({ userIds: ids });
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* Filter by specific Farcaster ID
|
|
2837
|
+
* @param fid - Farcaster ID
|
|
2838
|
+
*/
|
|
2839
|
+
static fid(fid) {
|
|
2840
|
+
return new FilterExpression({ fid });
|
|
2841
|
+
}
|
|
2842
|
+
/**
|
|
2843
|
+
* Filter by multiple Farcaster IDs
|
|
2844
|
+
* @param fids - Array of Farcaster IDs (max 1000)
|
|
2845
|
+
*/
|
|
2846
|
+
static fids(fids) {
|
|
2847
|
+
if (fids.length === 0) {
|
|
2848
|
+
throw new Error("fids requires at least one FID");
|
|
2849
|
+
}
|
|
2850
|
+
if (fids.length > 1e3) {
|
|
2851
|
+
throw new Error("fids cannot exceed 1000 FIDs");
|
|
2852
|
+
}
|
|
2853
|
+
return new FilterExpression({ fids });
|
|
2854
|
+
}
|
|
2855
|
+
// ===========================================================================
|
|
2856
|
+
// Tag Filters
|
|
2857
|
+
// ===========================================================================
|
|
2858
|
+
/**
|
|
2859
|
+
* Filter users with a specific tag
|
|
2860
|
+
* @param tag - Tag name (max 64 chars)
|
|
2861
|
+
*/
|
|
2862
|
+
static hasTag(tag) {
|
|
2863
|
+
if (tag.length > 64) {
|
|
2864
|
+
throw new Error("Tag must be 64 characters or less");
|
|
2865
|
+
}
|
|
2866
|
+
return new FilterExpression({ hasTag: tag });
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Filter users with any of the specified tags
|
|
2870
|
+
* @param tags - Array of tag names (1-20 tags)
|
|
2871
|
+
*/
|
|
2872
|
+
static hasAnyTag(tags) {
|
|
2873
|
+
if (tags.length === 0 || tags.length > 20) {
|
|
2874
|
+
throw new Error("hasAnyTag requires 1-20 tags");
|
|
2875
|
+
}
|
|
2876
|
+
return new FilterExpression({ hasAnyTag: tags });
|
|
2877
|
+
}
|
|
2878
|
+
/**
|
|
2879
|
+
* Filter users with all of the specified tags
|
|
2880
|
+
* @param tags - Array of tag names (1-20 tags)
|
|
2881
|
+
*/
|
|
2882
|
+
static hasAllTags(tags) {
|
|
2883
|
+
if (tags.length === 0 || tags.length > 20) {
|
|
2884
|
+
throw new Error("hasAllTags requires 1-20 tags");
|
|
2885
|
+
}
|
|
2886
|
+
return new FilterExpression({ hasAllTags: tags });
|
|
2887
|
+
}
|
|
2888
|
+
// ===========================================================================
|
|
2889
|
+
// Time-based Filters
|
|
2890
|
+
// ===========================================================================
|
|
2891
|
+
/**
|
|
2892
|
+
* Filter users created after a specific date
|
|
2893
|
+
* @param date - Date or ISO 8601 string
|
|
2894
|
+
*/
|
|
2895
|
+
static createdAfter(date) {
|
|
2896
|
+
const isoString = date instanceof Date ? date.toISOString() : date;
|
|
2897
|
+
return new FilterExpression({ createdAfter: isoString });
|
|
2898
|
+
}
|
|
2899
|
+
/**
|
|
2900
|
+
* Filter users created before a specific date
|
|
2901
|
+
* @param date - Date or ISO 8601 string
|
|
2902
|
+
*/
|
|
2903
|
+
static createdBefore(date) {
|
|
2904
|
+
const isoString = date instanceof Date ? date.toISOString() : date;
|
|
2905
|
+
return new FilterExpression({ createdBefore: isoString });
|
|
2906
|
+
}
|
|
2907
|
+
/**
|
|
2908
|
+
* Filter users who were last active after a specific date
|
|
2909
|
+
* @param date - Date or ISO 8601 string
|
|
2910
|
+
*/
|
|
2911
|
+
static lastActiveAfter(date) {
|
|
2912
|
+
const isoString = date instanceof Date ? date.toISOString() : date;
|
|
2913
|
+
return new FilterExpression({ lastActiveAfter: isoString });
|
|
2914
|
+
}
|
|
2915
|
+
// ===========================================================================
|
|
2916
|
+
// Reward/Quest Filters
|
|
2917
|
+
// ===========================================================================
|
|
2918
|
+
/**
|
|
2919
|
+
* Filter users who completed a specific reward
|
|
2920
|
+
* @param rewardId - Reward ID
|
|
2921
|
+
*/
|
|
2922
|
+
static completedReward(rewardId) {
|
|
2923
|
+
return new FilterExpression({ completedReward: rewardId });
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* Filter users who have NOT completed a specific reward
|
|
2927
|
+
* @param rewardId - Reward ID
|
|
2928
|
+
*/
|
|
2929
|
+
static notCompletedReward(rewardId) {
|
|
2930
|
+
return new FilterExpression({ notCompletedReward: rewardId });
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Filter users who completed a specific quest
|
|
2934
|
+
* @param questId - Quest ID
|
|
2935
|
+
*/
|
|
2936
|
+
static completedQuest(questId) {
|
|
2937
|
+
return new FilterExpression({ completedQuest: questId });
|
|
2938
|
+
}
|
|
2939
|
+
/**
|
|
2940
|
+
* Filter users who have a specific achievement
|
|
2941
|
+
* @param achievementId - Achievement ID
|
|
2942
|
+
*/
|
|
2943
|
+
static hasAchievement(achievementId) {
|
|
2944
|
+
return new FilterExpression({ hasAchievement: achievementId });
|
|
2945
|
+
}
|
|
2946
|
+
// ===========================================================================
|
|
2947
|
+
// Geography Filters
|
|
2948
|
+
// ===========================================================================
|
|
2949
|
+
/**
|
|
2950
|
+
* Filter users by timezone
|
|
2951
|
+
* @param zones - Array of timezone options with UTC offset and optional range (max 24)
|
|
2952
|
+
*/
|
|
2953
|
+
static timezones(zones) {
|
|
2954
|
+
if (zones.length === 0) {
|
|
2955
|
+
throw new Error("timezones requires at least one timezone");
|
|
2956
|
+
}
|
|
2957
|
+
if (zones.length > 24) {
|
|
2958
|
+
throw new Error("timezones cannot exceed 24 entries");
|
|
2959
|
+
}
|
|
2960
|
+
for (const zone of zones) {
|
|
2961
|
+
if (zone.offset < -12 || zone.offset > 14) {
|
|
2962
|
+
throw new Error("timezone offset must be between -12 and 14");
|
|
2963
|
+
}
|
|
2964
|
+
if (zone.range !== void 0 && (zone.range < 0 || zone.range > 12)) {
|
|
2965
|
+
throw new Error("timezone range must be between 0 and 12");
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
return new FilterExpression({ timezones: zones });
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* Filter users by country (ISO 3166-1 alpha-2 codes)
|
|
2972
|
+
* @param codes - Array of ISO 3166-1 alpha-2 country codes (e.g., ['US', 'CA', 'GB'])
|
|
2973
|
+
*/
|
|
2974
|
+
static countries(codes) {
|
|
2975
|
+
if (codes.length === 0) {
|
|
2976
|
+
throw new Error("countries requires at least one country code");
|
|
2977
|
+
}
|
|
2978
|
+
if (codes.length > 50) {
|
|
2979
|
+
throw new Error("countries cannot exceed 50 country codes");
|
|
2980
|
+
}
|
|
2981
|
+
for (const code of codes) {
|
|
2982
|
+
if (!/^[A-Z]{2}$/.test(code)) {
|
|
2983
|
+
throw new Error("Country codes must be ISO 3166-1 alpha-2 format (e.g., US, CA, GB)");
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
return new FilterExpression({ countries: codes.map((code) => ({ code })) });
|
|
2987
|
+
}
|
|
2988
|
+
// ===========================================================================
|
|
2989
|
+
// Engagement & Quality Filters
|
|
2990
|
+
// ===========================================================================
|
|
2991
|
+
/**
|
|
2992
|
+
* Filter users with quotient score >= minimum
|
|
2993
|
+
* @param score - Minimum quotient score (0-1, where 0.5=Casual, 0.75=Influential, 0.9=Exceptional)
|
|
2994
|
+
*/
|
|
2995
|
+
static quotientScoreMin(score) {
|
|
2996
|
+
if (score < 0 || score > 1) {
|
|
2997
|
+
throw new Error("quotientScoreMin must be between 0 and 1");
|
|
2998
|
+
}
|
|
2999
|
+
return new FilterExpression({ quotientScoreMin: score });
|
|
3000
|
+
}
|
|
3001
|
+
/**
|
|
3002
|
+
* Filter users with quotient score <= maximum
|
|
3003
|
+
* @param score - Maximum quotient score (0-1)
|
|
3004
|
+
*/
|
|
3005
|
+
static quotientScoreMax(score) {
|
|
3006
|
+
if (score < 0 || score > 1) {
|
|
3007
|
+
throw new Error("quotientScoreMax must be between 0 and 1");
|
|
3008
|
+
}
|
|
3009
|
+
return new FilterExpression({ quotientScoreMax: score });
|
|
3010
|
+
}
|
|
3011
|
+
/**
|
|
3012
|
+
* Filter users with quotient score in a range
|
|
3013
|
+
* @param min - Minimum score (0-1, optional)
|
|
3014
|
+
* @param max - Maximum score (0-1, optional)
|
|
3015
|
+
*/
|
|
3016
|
+
static quotientScoreRange(min, max) {
|
|
3017
|
+
if (min !== void 0 && (min < 0 || min > 1)) {
|
|
3018
|
+
throw new Error("quotientScoreRange min must be between 0 and 1");
|
|
3019
|
+
}
|
|
3020
|
+
if (max !== void 0 && (max < 0 || max > 1)) {
|
|
3021
|
+
throw new Error("quotientScoreRange max must be between 0 and 1");
|
|
3022
|
+
}
|
|
3023
|
+
const range = {};
|
|
3024
|
+
if (min !== void 0) range.min = min;
|
|
3025
|
+
if (max !== void 0) range.max = max;
|
|
3026
|
+
return new FilterExpression({ quotientScoreRange: range });
|
|
3027
|
+
}
|
|
3028
|
+
/**
|
|
3029
|
+
* Filter users with minimum battery percentage
|
|
3030
|
+
* @param pct - Minimum battery percentage (0-100)
|
|
3031
|
+
*/
|
|
3032
|
+
static minBatteryPercentage(pct) {
|
|
3033
|
+
if (pct < 0 || pct > 100) {
|
|
3034
|
+
throw new Error("minBatteryPercentage must be between 0 and 100");
|
|
3035
|
+
}
|
|
3036
|
+
return new FilterExpression({ minBatteryPercentage: pct });
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* Filter users who have recharged their battery within the last N days
|
|
3040
|
+
* @param days - Number of days
|
|
3041
|
+
*/
|
|
3042
|
+
static hasRechargedInLastDays(days) {
|
|
3043
|
+
if (days < 1) {
|
|
3044
|
+
throw new Error("hasRechargedInLastDays must be >= 1");
|
|
3045
|
+
}
|
|
3046
|
+
return new FilterExpression({ hasRechargedInLastDays: days });
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Exclude users who have already been pinged today
|
|
3050
|
+
*/
|
|
3051
|
+
static excludePingedToday() {
|
|
3052
|
+
return new FilterExpression({ excludePingedToday: true });
|
|
3053
|
+
}
|
|
3054
|
+
// ===========================================================================
|
|
3055
|
+
// Opt-In Filters
|
|
3056
|
+
// ===========================================================================
|
|
3057
|
+
/**
|
|
3058
|
+
* Filter to only users who have opted into lottery rewards
|
|
3059
|
+
*/
|
|
3060
|
+
static requireLotteryOptIn() {
|
|
3061
|
+
return new FilterExpression({ requireLotteryOptIn: true });
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Filter to only users who have opted into quiz rewards
|
|
3065
|
+
*/
|
|
3066
|
+
static requireQuizOptIn() {
|
|
3067
|
+
return new FilterExpression({ requireQuizOptIn: true });
|
|
3068
|
+
}
|
|
3069
|
+
// ===========================================================================
|
|
3070
|
+
// Activity Count Filters
|
|
3071
|
+
// ===========================================================================
|
|
3072
|
+
/**
|
|
3073
|
+
* Filter users with minimum cast count
|
|
3074
|
+
* @param count - Minimum number of casts
|
|
3075
|
+
*/
|
|
3076
|
+
static minCastCount(count) {
|
|
3077
|
+
if (count < 0) {
|
|
3078
|
+
throw new Error("minCastCount must be >= 0");
|
|
3079
|
+
}
|
|
3080
|
+
return new FilterExpression({ minCastCount: count });
|
|
3081
|
+
}
|
|
3082
|
+
// ===========================================================================
|
|
3083
|
+
// Ordering & Pagination
|
|
3084
|
+
// ===========================================================================
|
|
3085
|
+
/**
|
|
3086
|
+
* Set the order for results
|
|
3087
|
+
* @param order - Order option for sorting results
|
|
3088
|
+
*/
|
|
3089
|
+
static orderBy(order) {
|
|
3090
|
+
return new FilterExpression({ orderBy: order });
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Limit the number of results
|
|
3094
|
+
* @param n - Maximum number of results
|
|
3095
|
+
* @deprecated Limit is controlled by budgetCap, not this filter. This method will be removed.
|
|
3096
|
+
*/
|
|
3097
|
+
static limit(n) {
|
|
3098
|
+
console.warn("FilterBuilder.limit() is deprecated. Use budgetCap to control result count.");
|
|
3099
|
+
if (n < 1 || n > 1e4) {
|
|
3100
|
+
throw new Error("limit must be between 1 and 10000");
|
|
3101
|
+
}
|
|
3102
|
+
return new FilterExpression({ limit: n });
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
var GasTierSchema = zod.z.enum(["slow", "standard", "fast"]);
|
|
3106
|
+
var QuoteOptionsSchema = zod.z.object({
|
|
3107
|
+
draftId: zod.z.string().uuid(),
|
|
3108
|
+
amount: zod.z.string().regex(/^\d+(\.\d+)?$/).optional(),
|
|
3109
|
+
gasTier: GasTierSchema.optional(),
|
|
3110
|
+
slippageTolerance: zod.z.number().min(0).max(1).optional()
|
|
3111
|
+
});
|
|
3112
|
+
var QuoteRecipientSchema = zod.z.object({
|
|
3113
|
+
address: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address"),
|
|
3114
|
+
amount: zod.z.string(),
|
|
3115
|
+
share: zod.z.number().min(0).max(1)
|
|
3116
|
+
});
|
|
3117
|
+
var QuoteSchema2 = zod.z.object({
|
|
3118
|
+
id: zod.z.string().uuid(),
|
|
3119
|
+
draftId: zod.z.string().uuid(),
|
|
3120
|
+
totalAmount: zod.z.string(),
|
|
3121
|
+
estimatedGas: zod.z.string(),
|
|
3122
|
+
gasPrice: zod.z.string(),
|
|
3123
|
+
networkFee: zod.z.string(),
|
|
3124
|
+
platformFee: zod.z.string(),
|
|
3125
|
+
totalCost: zod.z.string(),
|
|
3126
|
+
token: TokenTypeSchema,
|
|
3127
|
+
network: NetworkSchema,
|
|
3128
|
+
recipients: zod.z.array(QuoteRecipientSchema),
|
|
3129
|
+
recipientCount: zod.z.number().int().positive(),
|
|
3130
|
+
expiresAt: zod.z.string().datetime(),
|
|
3131
|
+
createdAt: zod.z.string().datetime(),
|
|
3132
|
+
isValid: zod.z.boolean()
|
|
3133
|
+
});
|
|
3134
|
+
var ConfirmResultSchema2 = zod.z.object({
|
|
3135
|
+
valid: zod.z.boolean(),
|
|
3136
|
+
invalidReason: zod.z.string().optional(),
|
|
3137
|
+
currentGasEstimate: zod.z.string(),
|
|
3138
|
+
priceChange: zod.z.string(),
|
|
3139
|
+
canExecute: zod.z.boolean()
|
|
3140
|
+
});
|
|
3141
|
+
var ExecuteStatusSchema = zod.z.enum(["pending", "confirmed", "failed"]);
|
|
3142
|
+
var ExecuteResultSchema2 = zod.z.object({
|
|
3143
|
+
id: zod.z.string().uuid(),
|
|
3144
|
+
quoteId: zod.z.string().uuid(),
|
|
3145
|
+
transactionHash: zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/),
|
|
3146
|
+
status: ExecuteStatusSchema,
|
|
3147
|
+
blockNumber: zod.z.number().int().positive().optional(),
|
|
3148
|
+
initiatedAt: zod.z.string().datetime(),
|
|
3149
|
+
confirmedAt: zod.z.string().datetime().optional(),
|
|
3150
|
+
confirmations: zod.z.number().int().min(0),
|
|
3151
|
+
receiptId: zod.z.string().uuid().optional()
|
|
3152
|
+
});
|
|
3153
|
+
function validateQuoteOptions(options) {
|
|
3154
|
+
return QuoteOptionsSchema.safeParse(options);
|
|
3155
|
+
}
|
|
3156
|
+
function parseQuoteOptions(options) {
|
|
3157
|
+
return QuoteOptionsSchema.parse(options);
|
|
3158
|
+
}
|
|
3159
|
+
function validateQuote(quote) {
|
|
3160
|
+
return QuoteSchema2.safeParse(quote);
|
|
3161
|
+
}
|
|
3162
|
+
function parseQuote(quote) {
|
|
3163
|
+
return QuoteSchema2.parse(quote);
|
|
3164
|
+
}
|
|
3165
|
+
function validateExecuteResult(result) {
|
|
3166
|
+
return ExecuteResultSchema2.safeParse(result);
|
|
3167
|
+
}
|
|
3168
|
+
function parseExecuteResult(result) {
|
|
3169
|
+
return ExecuteResultSchema2.parse(result);
|
|
3170
|
+
}
|
|
3171
|
+
var TransferStatusSchema = zod.z.enum(["success", "failed"]);
|
|
3172
|
+
var ReceiptTransferSchema = zod.z.object({
|
|
3173
|
+
recipient: zod.z.string().regex(/^0x[a-fA-F0-9]{40}$/),
|
|
3174
|
+
amount: zod.z.string(),
|
|
3175
|
+
status: TransferStatusSchema,
|
|
3176
|
+
logIndex: zod.z.number().int().min(0)
|
|
3177
|
+
});
|
|
3178
|
+
var ReceiptSchema2 = zod.z.object({
|
|
3179
|
+
id: zod.z.string().uuid(),
|
|
3180
|
+
executionId: zod.z.string().uuid(),
|
|
3181
|
+
quoteId: zod.z.string().uuid(),
|
|
3182
|
+
draftId: zod.z.string().uuid(),
|
|
3183
|
+
transactionHash: zod.z.string().regex(/^0x[a-fA-F0-9]{64}$/),
|
|
3184
|
+
blockNumber: zod.z.number().int().positive(),
|
|
3185
|
+
blockTimestamp: zod.z.string().datetime(),
|
|
3186
|
+
network: NetworkSchema,
|
|
3187
|
+
token: TokenTypeSchema,
|
|
3188
|
+
totalAmount: zod.z.string(),
|
|
3189
|
+
gasUsed: zod.z.string(),
|
|
3190
|
+
gasPrice: zod.z.string(),
|
|
3191
|
+
gasCost: zod.z.string(),
|
|
3192
|
+
transfers: zod.z.array(ReceiptTransferSchema),
|
|
3193
|
+
successfulTransfers: zod.z.number().int().min(0),
|
|
3194
|
+
failedTransfers: zod.z.number().int().min(0),
|
|
3195
|
+
createdAt: zod.z.string().datetime()
|
|
3196
|
+
});
|
|
3197
|
+
function validateReceipt(receipt) {
|
|
3198
|
+
return ReceiptSchema2.safeParse(receipt);
|
|
3199
|
+
}
|
|
3200
|
+
function parseReceipt(receipt) {
|
|
3201
|
+
return ReceiptSchema2.parse(receipt);
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
exports.API_BASE_URLS = API_BASE_URLS;
|
|
3205
|
+
exports.API_KEY_PREFIXES = API_KEY_PREFIXES;
|
|
3206
|
+
exports.ActiveInLastDaysFilterSchema = ActiveInLastDaysFilterSchema;
|
|
3207
|
+
exports.AgentClient = AgentClient;
|
|
3208
|
+
exports.ApiQuoteResponseSchema = ApiQuoteResponseSchema;
|
|
3209
|
+
exports.ApiReceiptResponseSchema = ApiReceiptResponseSchema;
|
|
3210
|
+
exports.BeeperClient = BeeperClient;
|
|
3211
|
+
exports.BeeperEconomicsFilterSchema = BeeperEconomicsFilterSchema;
|
|
3212
|
+
exports.BeeperError = BeeperError;
|
|
3213
|
+
exports.CachedTokenHolderFilterSchema = CachedTokenHolderFilterSchema;
|
|
3214
|
+
exports.CachedTokenHolderSchema = CachedTokenHolderSchema;
|
|
3215
|
+
exports.ConfirmDepositResponseSchema = ConfirmDepositResponseSchema;
|
|
3216
|
+
exports.ConfirmResultSchema = ConfirmResultSchema2;
|
|
3217
|
+
exports.CountryFilterSchema = CountryFilterSchema;
|
|
3218
|
+
exports.DistributionStrategySchema = DistributionStrategySchema;
|
|
3219
|
+
exports.DraftInputSchema = DraftInputSchema;
|
|
3220
|
+
exports.DraftSchema = DraftSchema;
|
|
3221
|
+
exports.DraftStatusSchema = DraftStatusSchema;
|
|
3222
|
+
exports.DraftUpdateSchema = DraftUpdateSchema;
|
|
3223
|
+
exports.ENDPOINTS = ENDPOINTS;
|
|
3224
|
+
exports.ErrorCodes = ErrorCodes;
|
|
3225
|
+
exports.ExcludePingedTodayFilterSchema = ExcludePingedTodayFilterSchema;
|
|
3226
|
+
exports.ExcludeUsersFilterSchema = ExcludeUsersFilterSchema;
|
|
3227
|
+
exports.ExecuteResultSchema = ExecuteResultSchema2;
|
|
3228
|
+
exports.ExecuteSendResponseSchema = ExecuteSendResponseSchema;
|
|
3229
|
+
exports.ExecuteStatusSchema = ExecuteStatusSchema;
|
|
3230
|
+
exports.FILTER_SCHEMA = FILTER_SCHEMA;
|
|
3231
|
+
exports.FieldComparisonSchema = FieldComparisonSchema;
|
|
3232
|
+
exports.FilterBuilder = FilterBuilder;
|
|
3233
|
+
exports.FilterExpression = FilterExpression;
|
|
3234
|
+
exports.FilterExpressionSchema = FilterExpressionSchema;
|
|
3235
|
+
exports.FilterOperatorSchema = FilterOperatorSchema;
|
|
3236
|
+
exports.FilterValueSchema = FilterValueSchema;
|
|
3237
|
+
exports.FollowersOfFilterSchema = FollowersOfFilterSchema;
|
|
3238
|
+
exports.FollowingOfFilterSchema = FollowingOfFilterSchema;
|
|
3239
|
+
exports.GasTierSchema = GasTierSchema;
|
|
3240
|
+
exports.HEADERS = HEADERS;
|
|
3241
|
+
exports.HTTP_STATUS_TO_ERROR_CODE = HTTP_STATUS_TO_ERROR_CODE;
|
|
3242
|
+
exports.HasBaseWalletFilterSchema = HasBaseWalletFilterSchema;
|
|
3243
|
+
exports.HasRechargedInLastDaysFilterSchema = HasRechargedInLastDaysFilterSchema;
|
|
3244
|
+
exports.HasTierFilterSchema = HasTierFilterSchema;
|
|
3245
|
+
exports.HasVerifiedWalletFilterSchema = HasVerifiedWalletFilterSchema;
|
|
3246
|
+
exports.HttpClient = HttpClient;
|
|
3247
|
+
exports.IsWaitlistedFilterSchema = IsWaitlistedFilterSchema;
|
|
3248
|
+
exports.LegacyFieldComparisonSchema = LegacyFieldComparisonSchema;
|
|
3249
|
+
exports.LegacyFilterOperatorSchema = LegacyFilterOperatorSchema;
|
|
3250
|
+
exports.LegacyFilterValueSchema = LegacyFilterValueSchema;
|
|
3251
|
+
exports.LegacyRecipientFilterDSLSchema = LegacyRecipientFilterDSLSchema;
|
|
3252
|
+
exports.MaxAttentionPriceFilterSchema = MaxAttentionPriceFilterSchema;
|
|
3253
|
+
exports.MaxFidFilterSchema = MaxFidFilterSchema;
|
|
3254
|
+
exports.MaxFollowersFilterSchema = MaxFollowersFilterSchema;
|
|
3255
|
+
exports.MaxFollowingFilterSchema = MaxFollowingFilterSchema;
|
|
3256
|
+
exports.MinAttentionPriceFilterSchema = MinAttentionPriceFilterSchema;
|
|
3257
|
+
exports.MinBatteryPercentageFilterSchema = MinBatteryPercentageFilterSchema;
|
|
3258
|
+
exports.MinCastCountFilterSchema = MinCastCountFilterSchema;
|
|
3259
|
+
exports.MinClickThroughRateFilterSchema = MinClickThroughRateFilterSchema;
|
|
3260
|
+
exports.MinFidFilterSchema = MinFidFilterSchema;
|
|
3261
|
+
exports.MinFollowersFilterSchema = MinFollowersFilterSchema;
|
|
3262
|
+
exports.MinFollowingFilterSchema = MinFollowingFilterSchema;
|
|
3263
|
+
exports.MinProTenureDaysFilterSchema = MinProTenureDaysFilterSchema;
|
|
3264
|
+
exports.MinTenureDaysFilterSchema = MinTenureDaysFilterSchema;
|
|
3265
|
+
exports.MutualsWithFilterSchema = MutualsWithFilterSchema;
|
|
3266
|
+
exports.NetworkSchema = NetworkSchema;
|
|
3267
|
+
exports.NeynarScoreMaxFilterSchema = NeynarScoreMaxFilterSchema;
|
|
3268
|
+
exports.NeynarScoreMinFilterSchema = NeynarScoreMinFilterSchema;
|
|
3269
|
+
exports.OnchainFilterSchema = OnchainFilterSchema;
|
|
3270
|
+
exports.OrderBySchema = OrderBySchema;
|
|
3271
|
+
exports.PlatformFilterSchema = PlatformFilterSchema;
|
|
3272
|
+
exports.ProSubscriptionFilterSchema = ProSubscriptionFilterSchema;
|
|
3273
|
+
exports.QUOTE_EXPIRATION_SECONDS = QUOTE_EXPIRATION_SECONDS;
|
|
3274
|
+
exports.QuoteOptionsSchema = QuoteOptionsSchema;
|
|
3275
|
+
exports.QuoteRecipientSchema = QuoteRecipientSchema;
|
|
3276
|
+
exports.QuoteSchema = QuoteSchema2;
|
|
3277
|
+
exports.QuotientScoreMaxFilterSchema = QuotientScoreMaxFilterSchema;
|
|
3278
|
+
exports.QuotientScoreMinFilterSchema = QuotientScoreMinFilterSchema;
|
|
3279
|
+
exports.RETRYABLE_ERROR_CODES = RETRYABLE_ERROR_CODES;
|
|
3280
|
+
exports.RETRY_CONFIG = RETRY_CONFIG;
|
|
3281
|
+
exports.ReceiptSchema = ReceiptSchema2;
|
|
3282
|
+
exports.ReceiptTransactionSchema = ReceiptTransactionSchema;
|
|
3283
|
+
exports.ReceiptTransferSchema = ReceiptTransferSchema;
|
|
3284
|
+
exports.RecipientFilterDSLSchema = RecipientFilterDSLSchema;
|
|
3285
|
+
exports.RecipientFilterSchema = RecipientFilterSchema;
|
|
3286
|
+
exports.ReputationFilterSchema = ReputationFilterSchema;
|
|
3287
|
+
exports.RequireLotteryOptInFilterSchema = RequireLotteryOptInFilterSchema;
|
|
3288
|
+
exports.RequireQuizOptInFilterSchema = RequireQuizOptInFilterSchema;
|
|
3289
|
+
exports.RolesFilterSchema = RolesFilterSchema;
|
|
3290
|
+
exports.SDK_VERSION = SDK_VERSION;
|
|
3291
|
+
exports.SignalTokenFilterSchema = SignalTokenFilterSchema;
|
|
3292
|
+
exports.SocialFilterSchema = SocialFilterSchema;
|
|
3293
|
+
exports.SpamLabelFilterSchema = SpamLabelFilterSchema;
|
|
3294
|
+
exports.SpecificUsersFilterSchema = SpecificUsersFilterSchema;
|
|
3295
|
+
exports.TIMEOUTS = TIMEOUTS;
|
|
3296
|
+
exports.TimezoneFilterSchema = TimezoneFilterSchema;
|
|
3297
|
+
exports.TokenHolderDiscoverySchema = TokenHolderDiscoverySchema;
|
|
3298
|
+
exports.TokenHolderFilterSchema = TokenHolderFilterSchema;
|
|
3299
|
+
exports.TokenTypeSchema = TokenTypeSchema;
|
|
3300
|
+
exports.TransferStatusSchema = TransferStatusSchema;
|
|
3301
|
+
exports.VerifiedOnlyFilterSchema = VerifiedOnlyFilterSchema;
|
|
3302
|
+
exports.createAuthorizationHeader = createAuthorizationHeader;
|
|
3303
|
+
exports.createHttpConfig = createHttpConfig;
|
|
3304
|
+
exports.default = BeeperClient_default;
|
|
3305
|
+
exports.describeFilters = describeFilters;
|
|
3306
|
+
exports.generateDepositIdempotencyKey = generateDepositIdempotencyKey;
|
|
3307
|
+
exports.generateExecuteIdempotencyKey = generateExecuteIdempotencyKey;
|
|
3308
|
+
exports.generateFilterDocumentation = generateFilterDocumentation;
|
|
3309
|
+
exports.generateIdempotencyKey = generateIdempotencyKey;
|
|
3310
|
+
exports.getAllFilterNames = getAllFilterNames;
|
|
3311
|
+
exports.getApiKeyEnvironment = getApiKeyEnvironment;
|
|
3312
|
+
exports.getFilterSchema = getFilterSchema;
|
|
3313
|
+
exports.isRetryableCode = isRetryableCode;
|
|
3314
|
+
exports.isValidApiKeyFormat = isValidApiKeyFormat;
|
|
3315
|
+
exports.maskApiKey = maskApiKey;
|
|
3316
|
+
exports.parseDraft = parseDraft;
|
|
3317
|
+
exports.parseDraftInput = parseDraftInput;
|
|
3318
|
+
exports.parseExecuteResult = parseExecuteResult;
|
|
3319
|
+
exports.parseFilter = parseFilter;
|
|
3320
|
+
exports.parseQuote = parseQuote;
|
|
3321
|
+
exports.parseQuoteOptions = parseQuoteOptions;
|
|
3322
|
+
exports.parseReceipt = parseReceipt;
|
|
3323
|
+
exports.parseRecipientFilter = parseRecipientFilter;
|
|
3324
|
+
exports.safeParseFilter = safeParseFilter;
|
|
3325
|
+
exports.safeParseRecipientFilter = safeParseRecipientFilter;
|
|
3326
|
+
exports.sendConfirmDeposit = confirmDeposit;
|
|
3327
|
+
exports.sendCreateDraft = createDraft;
|
|
3328
|
+
exports.sendCreateQuote = createQuote;
|
|
3329
|
+
exports.sendExecuteSend = executeSend;
|
|
3330
|
+
exports.sendGetEstimatedTimeRemaining = getEstimatedTimeRemaining;
|
|
3331
|
+
exports.sendGetFailedTransactions = getFailedTransactions;
|
|
3332
|
+
exports.sendGetQuote = getQuote;
|
|
3333
|
+
exports.sendGetReceipt = getReceipt;
|
|
3334
|
+
exports.sendGetSuccessRate = getSuccessRate;
|
|
3335
|
+
exports.sendIsComplete = isComplete;
|
|
3336
|
+
exports.sendIsDepositSufficient = isDepositSufficient;
|
|
3337
|
+
exports.sendIsExecuting = isExecuting;
|
|
3338
|
+
exports.sendIsQuoteExpired = isQuoteExpired;
|
|
3339
|
+
exports.sendIsReadyForDeposit = isReadyForDeposit;
|
|
3340
|
+
exports.sendIsReadyForQuote = isReadyForQuote;
|
|
3341
|
+
exports.sendIsSuccess = isSuccess;
|
|
3342
|
+
exports.sendPollUntilComplete = pollUntilComplete;
|
|
3343
|
+
exports.sendUpdateDraft = updateDraft;
|
|
3344
|
+
exports.sendValidateDraftInput = validateDraftInput2;
|
|
3345
|
+
exports.validateDraft = validateDraft;
|
|
3346
|
+
exports.validateDraftInput = validateDraftInput;
|
|
3347
|
+
exports.validateExecuteResult = validateExecuteResult;
|
|
3348
|
+
exports.validateFilter = validateFilter;
|
|
3349
|
+
exports.validateFilterHasTargeting = validateFilterHasTargeting;
|
|
3350
|
+
exports.validateQuote = validateQuote;
|
|
3351
|
+
exports.validateQuoteOptions = validateQuoteOptions;
|
|
3352
|
+
exports.validateReceipt = validateReceipt;
|
|
3353
|
+
exports.validateRecipientFilter = validateRecipientFilter;
|
|
3354
|
+
//# sourceMappingURL=index.cjs.map
|
|
3355
|
+
//# sourceMappingURL=index.cjs.map
|