@fileverse-dev/formulajs 4.4.11-mod-82 → 4.4.11-mod-83-patch-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/esm/index.mjs CHANGED
@@ -13130,7 +13130,6 @@ const UTILITY = {
13130
13130
  };
13131
13131
  const MAX_PAGE_LIMIT = 250;
13132
13132
 
13133
- // if data block need API key
13134
13133
  const SERVICES_API_KEY = {
13135
13134
  Etherscan: 'Etherscan',
13136
13135
  Coingecko: 'Coingecko',
@@ -13187,419 +13186,86 @@ class InvalidApiKeyError extends Error {
13187
13186
  }
13188
13187
  }
13189
13188
 
13190
- let PROXY_MAP;
13191
-
13192
- function initializeProxyMap() {
13193
- console.log('initializeProxyMap', window.NEXT_PUBLIC_PROXY_BASE_URL);
13194
- if (!PROXY_MAP) {
13195
- const fileverseProxyUrl = `${window.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`;
13196
-
13197
- PROXY_MAP = {
13198
- Etherscan: {
13199
- url: `${fileverseProxyUrl}/proxy`,
13200
- removeParams: ['apikey']
13201
- },
13202
- Basescan: {
13203
- url: `${fileverseProxyUrl}/proxy`,
13204
- removeParams: ['apikey']
13205
- },
13206
- Gnosisscan: {
13207
- url: `${fileverseProxyUrl}/proxy`,
13208
- removeParams: ['apikey']
13209
- },
13210
- Coingecko: {
13211
- url: `${fileverseProxyUrl}/proxy`,
13212
- removeParams: ['apikey']
13213
- },
13214
- Firefly: {
13215
- url: `${fileverseProxyUrl}/proxy`,
13216
- removeParams: ['apikey']
13217
- },
13218
- Neynar: {
13219
- url: `${fileverseProxyUrl}/proxy`,
13220
- removeParams: ['api_key']
13221
- },
13222
- Safe: {
13223
- url: `${fileverseProxyUrl}/proxy`,
13224
- removeParams: ['api_key']
13225
- },
13226
- Defillama: {
13227
- url: `${fileverseProxyUrl}/proxy`,
13228
- removeParams: ['api_key']
13229
- },
13230
- GnosisPay: {
13231
- url: `${fileverseProxyUrl}/proxy`,
13232
- removeParams: ['api_key']
13233
- },
13234
- // Add more services as needed. It can be direct url instead of ENV variable
13235
- // ANOTHER_SERVICE: "https://another-proxy-url.com"
13236
- };
13237
- }
13238
- return PROXY_MAP;
13239
- }
13240
-
13241
- // const fileverseProxyUrl = `${window.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`
13242
- // // Proxy map configuration
13243
- // const PROXY_MAP = {
13244
- // Etherscan: {
13245
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13246
- // removeParams: ['apikey']
13247
- // },
13248
- // Basescan: {
13249
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13250
- // removeParams: ['apikey']
13251
- // },
13252
- // Gnosisscan: {
13253
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13254
- // removeParams: ['apikey']
13255
- // },
13256
- // Coingecko: {
13257
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13258
- // removeParams: ['apikey']
13259
- // },
13260
- // Firefly: {
13261
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13262
- // removeParams: ['apikey']
13263
- // },
13264
- // Neynar: {
13265
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13266
- // removeParams: ['api_key']
13267
- // },
13268
- // Safe: {
13269
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13270
- // removeParams: ['api_key']
13271
- // },
13272
- // Defillama: {
13273
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13274
- // removeParams: ['api_key']
13275
- // },
13276
- // GnosisPay: {
13277
- // url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
13278
- // removeParams: ['api_key']
13279
- // },
13280
- // // Add more services as needed. It can be direct url instead of ENV variable
13281
- // // ANOTHER_SERVICE: "https://another-proxy-url.com"
13282
- // };
13283
-
13284
- /**
13285
- * Removes specified parameters from a URL
13286
- * @param {string} url - The original URL
13287
- * @param {string[]} paramsToRemove - Array of parameter names to remove
13288
- * @returns {string} URL with specified parameters removed
13289
- */
13290
- function removeUrlParams(url, paramsToRemove) {
13291
- if (!paramsToRemove || paramsToRemove.length === 0) {
13292
- return url;
13189
+ const errorMessageHandler = (err, functionName) => {
13190
+
13191
+ switch (true) {
13192
+ case err instanceof ValidationError : {
13193
+ return {
13194
+ message: err.message,
13195
+ functionName,
13196
+ type: ERROR_MESSAGES_FLAG.INVALID_PARAM
13197
+ }
13293
13198
  }
13294
13199
 
13295
- const urlObj = new URL(url);
13296
-
13297
- paramsToRemove.forEach(param => {
13298
- if (urlObj.searchParams.has(param)) {
13299
- urlObj.searchParams.delete(param);
13300
- }
13301
- });
13302
-
13303
- return urlObj.toString();
13304
- }
13305
-
13306
- /**
13307
- * Handles URL routing through proxy or direct API calls
13308
- * @param {string} url - The original API URL
13309
- * @param {string} serviceName - [OPTIONAL] The name of the service (e.g., 'EOA')
13310
- * @param {object} headers - [OPTIONAL] The name of the service (e.g., 'EOA')
13311
- * @returns {Object} Object containing URL and HEADERS for the fetch request
13312
- */
13313
- function getUrlAndHeaders({ url, serviceName, headers = {} }) {
13314
- const proxyMap = initializeProxyMap();
13315
- // Check if proxy is enabled in localStorage
13316
- const apiKeyLS = window.localStorage.getItem(SERVICES_API_KEY[serviceName]);
13317
- const isProxyModeEnabledValue = apiKeyLS === 'DEFAULT_PROXY_MODE';
13318
-
13319
- // Check if proxy URL exists for this service
13320
- const proxyConfig = proxyMap[serviceName];
13321
-
13322
- if (!proxyConfig && SERVICES_API_KEY[serviceName] && (!apiKeyLS || apiKeyLS === '')) {
13323
- throw new MissingApiKeyError(SERVICES_API_KEY[serviceName])
13324
- }
13200
+ case err instanceof MissingApiKeyError :
13201
+ return {
13202
+ message: err.message,
13203
+ functionName,
13204
+ type: ERROR_MESSAGES_FLAG.MISSING_KEY
13205
+ }
13325
13206
 
13326
- // If proxy mode is enabled AND proxy URL exists for this service
13327
- if ((isProxyModeEnabledValue || !apiKeyLS || apiKeyLS === '') && proxyConfig) {
13328
- // Remove specified parameters from the target URL
13329
- const cleanedUrl = removeUrlParams(url, proxyConfig.removeParams);
13207
+ case err instanceof RateLimitError:
13208
+ return {
13209
+ message: err.message,
13210
+ functionName,
13211
+ type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13212
+ apiKeyName: err.api
13213
+ }
13330
13214
 
13215
+ case err instanceof NetworkError:
13216
+ if(err.status === 429){
13331
13217
  return {
13332
- URL: proxyConfig.url,
13333
- HEADERS: {
13334
- 'target-url': cleanedUrl,
13335
- method: 'GET',
13336
- 'Content-Type': 'application/json'
13337
- }
13338
- };
13339
- }
13340
-
13341
- return {
13342
- URL: url,
13343
- HEADERS: {
13344
- ...headers,
13345
- }
13346
- };
13347
- }
13348
-
13349
- const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
13350
- if (!timestamp || !chain) return
13351
- const chainId = CHAIN_ID_MAP[chain];
13352
- const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
13353
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
13354
- const res = await fetch(finalUrl, {
13355
- method: 'GET',
13356
- headers: HEADERS,
13357
- });
13358
- const json = await res.json();
13359
- return parseInt(json.result);
13360
-
13361
- };
13362
-
13363
- var fromTimestampToBlock = {
13364
- fromTimeStampToBlock
13365
- };
13218
+ message: err.message,
13219
+ functionName,
13220
+ type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13221
+ apiKeyName: err.api
13222
+ }
13223
+ } else {
13224
+ return {
13225
+ message: err.message,
13226
+ functionName,
13227
+ type: ERROR_MESSAGES_FLAG.NETWORK_ERROR,
13228
+ apiKeyName: err.api
13229
+ }
13230
+ }
13366
13231
 
13367
- function toTimestamp(dateStr) {
13368
- // Expecting format: "DD/MM/YYYY"
13369
- const [day, month, year] = dateStr.split("/").map(Number);
13370
- const date = new Date(year, month - 1, day);
13371
- return Math.floor(date.getTime() / 1000); // Unix timestamp in seconds
13372
- }
13232
+ case err instanceof EnsError:
13233
+ return {
13234
+ message: err.message,
13235
+ functionName,
13236
+ type: ERROR_MESSAGES_FLAG.ENS
13237
+ }
13373
13238
 
13374
- const isAddress = (input) => {
13375
- return (/^0x[a-fA-F0-9]{40}$/.test(input))
13376
- };
13239
+ case err instanceof InvalidApiKeyError:
13240
+ return {
13241
+ message: err.message,
13242
+ functionName,
13243
+ type: ERROR_MESSAGES_FLAG.INVALID_API_KEY,
13244
+ apiKeyName: err.api
13245
+ }
13377
13246
 
13378
- var isAddress$1 = {
13379
- isAddress
13247
+ default:
13248
+ return {
13249
+ message: 'An unexpected error occured',
13250
+ functionName,
13251
+ type: ERROR_MESSAGES_FLAG.DEFAULT,
13252
+ reason: err.message || err
13253
+ }
13254
+ }
13380
13255
  };
13381
13256
 
13382
- /* global document */
13383
- /* global window */
13384
- /* global ethers */
13385
-
13386
13257
 
13387
13258
 
13388
- async function fromEnsNameToAddress(name) {
13389
- if (typeof ethers === 'undefined') {
13390
- await new Promise((resolve, reject) => {
13391
- const script = document.createElement('script');
13392
- script.src = 'https://cdn.jsdelivr.net/npm/ethers@6.10.0/dist/ethers.umd.min.js';
13393
- script.onload = resolve;
13394
- script.onerror = reject;
13395
- document.head.appendChild(script);
13396
- });
13397
- }
13398
-
13399
- const ALCHEMY_KEY = window.localStorage.getItem(UTILITY.ALCHEMY_API_KEY);
13400
- if (!ALCHEMY_KEY) {
13401
- console.error('alchemy api key missing');
13402
- return null
13403
- }
13404
- const provider = new ethers.AlchemyProvider('mainnet', ALCHEMY_KEY);
13405
13259
 
13406
- try {
13407
- const resolved = await provider.resolveName(name); // ENS ➝ address
13408
- return resolved || null
13409
- } catch (err) {
13410
- console.error('ENS resolution failed:', err.message);
13411
- return null
13260
+ function validateParams(schema, rawParams) {
13261
+ const result = schema.safeParse(rawParams);
13262
+ if (!result.success) {
13263
+ const issue = result.error.issues[0];
13264
+ throw new ValidationError(
13265
+ issue.message
13266
+ )
13412
13267
  }
13413
- }
13414
- const validateAndGetAddress = async (address) => {
13415
- if(isAddress$1.isAddress(address)) return address
13416
-
13417
- const resolvedAddress = await fromEnsNameToAddress(address);
13418
- if(resolvedAddress) return resolvedAddress
13419
- throw new ValidationError("Invalid address")
13420
- };
13421
-
13422
- var fromEnsNameToAddress$1 = {
13423
- validateAndGetAddress
13424
- };
13425
-
13426
- async function handleScanRequest({
13427
- type,
13428
- address,
13429
- startDate,
13430
- endDate,
13431
- page = 1,
13432
- offset = 10,
13433
- apiKey,
13434
- functionName,
13435
- chainId,
13436
- network
13437
- }) {
13438
- const API_INFO_MAP = {
13439
- BASE: { url: 'https://api.basescan.org/api', apiKeyName: SERVICES_API_KEY.Basescan },
13440
- ETHERSCAN: { url: 'https://api.etherscan.io/v2/api', apiKeyName: SERVICES_API_KEY.Etherscan },
13441
- GNOSIS: { url: 'https://api.gnosisscan.io/api', apiKeyName: SERVICES_API_KEY.Gnosisscan }
13442
- };
13443
-
13444
- if (type !== 'gas') {
13445
- address = await fromEnsNameToAddress$1.validateAndGetAddress(address);
13446
- }
13447
-
13448
- const apiInfo = API_INFO_MAP[functionName];
13449
- const baseUrl = apiInfo?.url;
13450
- if (!baseUrl) throw new ValidationError(`Api not found for: ${functionName}`)
13451
-
13452
-
13453
- const ACTION_MAP = {
13454
- 'all-txns': 'txlist',
13455
- 'token-txns': 'tokentx',
13456
- 'nft-txns': 'tokennfttx',
13457
- 'gas': 'gasoracle'
13458
- };
13459
-
13460
- const action = ACTION_MAP[type];
13461
- if (!action) throw new ValidationError(`Invalid type: ${type}`)
13462
-
13463
- const module = action === 'gasoracle' ? 'gastracker' : 'account';
13464
- let url = `${baseUrl}?chainid=${chainId}&module=${module}&action=${action}&apikey=${apiKey}`;
13465
-
13466
- if (['all-txns', 'token-txns', 'nft-txns'].includes(type)) {
13467
- url += `&address=${address}&startblock=0&endblock=99999999&sort=asc`;
13468
-
13469
- if (!isNaN(startDate) && !isNaN(endDate)) {
13470
- const [startBlock, endBlock] = await Promise.all([
13471
- fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startDate), network, apiKey),
13472
- fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endDate), network, apiKey)
13473
- ]);
13474
- url += `&startblock=${startBlock || '0'}&endblock=${endBlock || '99999999'}`;
13475
- }
13476
- url += `&page=${page}&offset=${offset}`;
13477
- }
13478
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({url, serviceName: apiInfo.apiKeyName, headers: {}});
13479
- const res = await fetch(finalUrl, {
13480
- method: 'GET',
13481
- headers: HEADERS,
13482
- });
13483
- if (!res.ok) {
13484
- throw new NetworkError(apiInfo.apiKeyName, res.status)
13485
- }
13486
- const json = await res.json();
13487
-
13488
- if (typeof json.result === 'string') {
13489
- if (json.result.includes('Invalid API Key')){
13490
- throw new InvalidApiKeyError(apiInfo.apiKeyName)
13491
- }
13492
-
13493
- if (json.result.includes('Max rate limit reached'))
13494
- throw new RateLimitError(apiInfo.apiKeyName)
13495
- }
13496
-
13497
- return type === 'gas' && !Array.isArray(json.result) ? [json.result] : json.result
13498
- }
13499
-
13500
- const fromUsernameToFid = async (username, apiKey) => {
13501
- if (!username) return null
13502
- const url = `https://api.neynar.com/v2/farcaster/user/search/?q=${username}&limit=5`;
13503
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
13504
- url, serviceName: 'Neynar', headers: {
13505
- 'x-api-key': apiKey,
13506
- 'x-neynar-experimental': 'false'
13507
- }
13508
- });
13509
-
13510
- const res = await fetch(finalUrl, {
13511
- method: 'GET',
13512
- headers: HEADERS,
13513
- });
13514
- const json = await res.json();
13515
- const users = json.result ? json.result.users : [];
13516
- const user = users.find(user => user.username === username);
13517
- return user && user.fid || null;
13518
- };
13519
- var fromUsernameToFid$1 = {
13520
- fromUsernameToFid
13521
- };
13522
-
13523
- const errorMessageHandler = (err, functionName) => {
13524
-
13525
- switch (true) {
13526
- case err instanceof ValidationError : {
13527
- return {
13528
- message: err.message,
13529
- functionName,
13530
- type: ERROR_MESSAGES_FLAG.INVALID_PARAM
13531
- }
13532
- }
13533
-
13534
- case err instanceof MissingApiKeyError :
13535
- return {
13536
- message: err.message,
13537
- functionName,
13538
- type: ERROR_MESSAGES_FLAG.MISSING_KEY
13539
- }
13540
-
13541
- case err instanceof RateLimitError:
13542
- return {
13543
- message: err.message,
13544
- functionName,
13545
- type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13546
- apiKeyName: err.api
13547
- }
13548
-
13549
- case err instanceof NetworkError:
13550
- if(err.status === 429){
13551
- return {
13552
- message: err.message,
13553
- functionName,
13554
- type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13555
- apiKeyName: err.api
13556
- }
13557
- } else {
13558
- return {
13559
- message: err.message,
13560
- functionName,
13561
- type: ERROR_MESSAGES_FLAG.NETWORK_ERROR,
13562
- apiKeyName: err.api
13563
- }
13564
- }
13565
-
13566
- case err instanceof EnsError:
13567
- return {
13568
- message: err.message,
13569
- functionName,
13570
- type: ERROR_MESSAGES_FLAG.ENS
13571
- }
13572
-
13573
- case err instanceof InvalidApiKeyError:
13574
- return {
13575
- message: err.message,
13576
- functionName,
13577
- type: ERROR_MESSAGES_FLAG.INVALID_API_KEY,
13578
- apiKeyName: err.api
13579
- }
13580
-
13581
- default:
13582
- return {
13583
- message: 'An unexpected error occured',
13584
- functionName,
13585
- type: ERROR_MESSAGES_FLAG.DEFAULT,
13586
- reason: err.message || err
13587
- }
13588
- }
13589
- };
13590
-
13591
-
13592
-
13593
-
13594
- function validateParams(schema, rawParams) {
13595
- const result = schema.safeParse(rawParams);
13596
- if (!result.success) {
13597
- const issue = result.error.issues[0];
13598
- throw new ValidationError(
13599
- issue.message
13600
- )
13601
- }
13602
- return result.data
13268
+ return result.data
13603
13269
  }
13604
13270
 
13605
13271
  var util;
@@ -17518,51 +17184,50 @@ ZodOptional.create;
17518
17184
  ZodNullable.create;
17519
17185
  const preprocessType = ZodEffects.createWithPreprocess;
17520
17186
 
17521
- const farcasterSchema = objectType({
17522
- platform: literalType('farcaster'),
17523
- contentType: enumType(['posts', 'replies', 'channels']),
17524
- identifier: stringType().nonempty(),
17525
- start: numberType().int().nonnegative().default(0),
17526
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17187
+ const aaveParamsSchema = objectType({
17188
+ graphType: enumType(['v2','v2-raw']),
17189
+ category: enumType(['tokens','markets']),
17190
+ param1: stringType().nonempty(),
17191
+ param2: stringType().optional(),
17527
17192
  });
17528
17193
 
17529
- const lensSchema = objectType({
17530
- platform: literalType('lens'),
17531
- contentType: enumType(['posts', 'replies']),
17532
- identifier: stringType().nonempty(),
17533
- start: numberType().int().nonnegative().default(0),
17534
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17535
- });
17194
+ async function AAVE() {
17195
+ try {
17536
17196
 
17537
- const fireflyParamsSchema = discriminatedUnionType('platform', [
17538
- farcasterSchema,
17539
- lensSchema,
17540
- ]);
17541
- const fireFlyPlaformType = {
17542
- farcaster: {
17543
- posts: 'farcasterid',
17544
- replies: 'farcasterpostid',
17545
- channels: 'farcasterchannels'
17546
- },
17547
- lens: {
17548
- posts: 'lensid',
17549
- replies: 'lenspostid'
17550
- }
17551
- };
17197
+ const [graphType, category, param1, param2] = argsToArray(arguments);
17552
17198
 
17553
- const lensParamsSchema = objectType({
17554
- contentType: enumType(['posts', 'replies']),
17555
- identifier: stringType().nonempty(),
17556
- start: numberType().int().nonnegative().default(0),
17557
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17558
- });
17559
17199
 
17560
- const farcasterParamsSchema = objectType({
17561
- contentType: enumType(['posts', 'replies', 'channels']),
17562
- identifier: stringType().nonempty(),
17563
- start: numberType().int().nonnegative().default(0),
17564
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17565
- });
17200
+ validateParams(aaveParamsSchema, { graphType, category, param1, param2 });
17201
+
17202
+ const baseUrl = 'https://onchain-proxy.fileverse.io/third-party';
17203
+ const url =
17204
+ `${baseUrl}` +
17205
+ `?service=aave` +
17206
+ `&graphType=${encodeURIComponent(graphType)}` +
17207
+ `&category=${encodeURIComponent(category)}` +
17208
+ `&input1=${encodeURIComponent(param1)}` +
17209
+ (param2 ? `&input2=${encodeURIComponent(param2)}` : '');
17210
+
17211
+ const res = await fetch(url);
17212
+ if (!res.ok) {
17213
+ throw new NetworkError('AAVE', res.status)
17214
+ }
17215
+
17216
+ const json = await res.json();
17217
+ if (Array.isArray(json)) {
17218
+ return json.map(item => {
17219
+ const flat = {};
17220
+ Object.entries(item).forEach(([k, v]) => {
17221
+ if (v === null || typeof v !== 'object') flat[k] = v;
17222
+ });
17223
+ return flat
17224
+ })
17225
+ }
17226
+ return json
17227
+ } catch (err) {
17228
+ return errorMessageHandler(err, 'AAVE')
17229
+ }
17230
+ }
17566
17231
 
17567
17232
  const dateStringToTimestamp = (val) => {
17568
17233
  const [dd, mm, yyyy] = val.split('/');
@@ -17583,6 +17248,440 @@ const dateOrTimestamp = preprocessType(
17583
17248
  numberType({ invalid_type_error: 'Date must be a valid DD/MM/YYYY or timestamp' }).int('Date must be an integer timestamp').nonnegative('Date must be a nonnegative timestamp').refine((n) => !isNaN(n), { message: 'Invalid date format or value: expected DD/MM/YYYY' })
17584
17249
  );
17585
17250
 
17251
+ const baseSchema = objectType({
17252
+ addresses: stringType().nonempty(),
17253
+ category: enumType(['balance','txns']),
17254
+ chains: preprocessType(
17255
+ (val) => typeof val === 'string'
17256
+ ? val.split(',').map(s => s.trim()).filter(Boolean)
17257
+ : val,
17258
+ arrayType(
17259
+ enumType(['ethereum','gnosis','base'])
17260
+ ).nonempty()
17261
+ ),
17262
+ startTime: dateOrTimestamp.optional(),
17263
+ endTime: dateOrTimestamp.optional(),
17264
+ page: numberType().int().nonnegative().default(1),
17265
+ offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17266
+ });
17267
+
17268
+ const eoaParamsSchema = preprocessType(
17269
+ (val) => {
17270
+ const obj = { ...(val || {}) };
17271
+ // if balance, ignore startTime/endTime
17272
+ if (obj.category === 'balance') {
17273
+ delete obj.startTime;
17274
+ delete obj.endTime;
17275
+ }
17276
+ return obj
17277
+ },
17278
+ baseSchema.refine(data => {
17279
+ // for txns, startTime and endTime are required
17280
+ if (data.category === 'txns') {
17281
+ return data.startTime !== undefined && data.endTime !== undefined
17282
+ }
17283
+ return true
17284
+ }, {
17285
+ message: 'startTime and endTime required for txns',
17286
+ path: ['startTime'],
17287
+ })
17288
+ );
17289
+
17290
+ const isAddress = (input) => {
17291
+ return (/^0x[a-fA-F0-9]{40}$/.test(input))
17292
+ };
17293
+
17294
+ var isAddress$1 = {
17295
+ isAddress
17296
+ };
17297
+
17298
+ /* global document */
17299
+ /* global window */
17300
+ /* global ethers */
17301
+
17302
+
17303
+
17304
+ async function fromEnsNameToAddress(name) {
17305
+ if (typeof ethers === 'undefined') {
17306
+ await new Promise((resolve, reject) => {
17307
+ const script = document.createElement('script');
17308
+ script.src = 'https://cdn.jsdelivr.net/npm/ethers@6.10.0/dist/ethers.umd.min.js';
17309
+ script.onload = resolve;
17310
+ script.onerror = reject;
17311
+ document.head.appendChild(script);
17312
+ });
17313
+ }
17314
+
17315
+ const ALCHEMY_KEY = window.localStorage.getItem(UTILITY.ALCHEMY_API_KEY);
17316
+ if (!ALCHEMY_KEY) {
17317
+ console.error('alchemy api key missing');
17318
+ return null
17319
+ }
17320
+ const provider = new ethers.AlchemyProvider('mainnet', ALCHEMY_KEY);
17321
+
17322
+ try {
17323
+ const resolved = await provider.resolveName(name); // ENS ➝ address
17324
+ return resolved || null
17325
+ } catch (err) {
17326
+ console.error('ENS resolution failed:', err.message);
17327
+ return null
17328
+ }
17329
+ }
17330
+ const validateAndGetAddress = async (address) => {
17331
+ if(isAddress$1.isAddress(address)) return address
17332
+
17333
+ const resolvedAddress = await fromEnsNameToAddress(address);
17334
+ if(resolvedAddress) return resolvedAddress
17335
+ throw new ValidationError("Invalid address")
17336
+ };
17337
+
17338
+ var fromEnsNameToAddress$1 = {
17339
+ validateAndGetAddress
17340
+ };
17341
+
17342
+ let PROXY_MAP;
17343
+
17344
+ function initializeProxyMap() {
17345
+ if (!PROXY_MAP) {
17346
+ const fileverseProxyUrl = `${window.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`;
17347
+
17348
+ PROXY_MAP = {
17349
+ Etherscan: {
17350
+ url: fileverseProxyUrl,
17351
+ removeParams: ['apikey']
17352
+ },
17353
+ Basescan: {
17354
+ url: fileverseProxyUrl,
17355
+ removeParams: ['apikey']
17356
+ },
17357
+ Gnosisscan: {
17358
+ url: fileverseProxyUrl,
17359
+ removeParams: ['apikey']
17360
+ },
17361
+ Coingecko: {
17362
+ url: fileverseProxyUrl,
17363
+ removeParams: ['apikey']
17364
+ },
17365
+ Firefly: {
17366
+ url: fileverseProxyUrl,
17367
+ removeParams: ['apikey']
17368
+ },
17369
+ Neynar: {
17370
+ url: fileverseProxyUrl,
17371
+ removeParams: ['api_key']
17372
+ },
17373
+ Safe: {
17374
+ url: fileverseProxyUrl,
17375
+ removeParams: ['api_key']
17376
+ },
17377
+ Defillama: {
17378
+ url: fileverseProxyUrl,
17379
+ removeParams: ['api_key']
17380
+ },
17381
+ GnosisPay: {
17382
+ url: fileverseProxyUrl,
17383
+ removeParams: ['api_key']
17384
+ },
17385
+ // Add more services as needed. It can be direct url instead of ENV variable
17386
+ // ANOTHER_SERVICE: "https://another-proxy-url.com"
17387
+ };
17388
+ }
17389
+ return PROXY_MAP;
17390
+ }
17391
+
17392
+ /**
17393
+ * Removes specified parameters from a URL
17394
+ * @param {string} url - The original URL
17395
+ * @param {string[]} paramsToRemove - Array of parameter names to remove
17396
+ * @returns {string} URL with specified parameters removed
17397
+ */
17398
+ function removeUrlParams(url, paramsToRemove) {
17399
+ if (!paramsToRemove || paramsToRemove.length === 0) {
17400
+ return url;
17401
+ }
17402
+
17403
+ const urlObj = new URL(url);
17404
+
17405
+ paramsToRemove.forEach(param => {
17406
+ if (urlObj.searchParams.has(param)) {
17407
+ urlObj.searchParams.delete(param);
17408
+ }
17409
+ });
17410
+
17411
+ return urlObj.toString();
17412
+ }
17413
+
17414
+ /**
17415
+ * Handles URL routing through proxy or direct API calls
17416
+ * @param {string} url - The original API URL
17417
+ * @param {string} serviceName - [OPTIONAL] The name of the service (e.g., 'EOA')
17418
+ * @param {object} headers - [OPTIONAL] The name of the service (e.g., 'EOA')
17419
+ * @returns {Object} Object containing URL and HEADERS for the fetch request
17420
+ */
17421
+ function getUrlAndHeaders({ url, serviceName, headers = {} }) {
17422
+ const proxyMap = initializeProxyMap();
17423
+ // Check if proxy is enabled in localStorage
17424
+ const apiKeyLS = window.localStorage.getItem(SERVICES_API_KEY[serviceName]);
17425
+ const isProxyModeEnabledValue = apiKeyLS === 'DEFAULT_PROXY_MODE';
17426
+
17427
+ // Check if proxy URL exists for this service
17428
+ const proxyConfig = proxyMap[serviceName];
17429
+
17430
+ if (!proxyConfig && SERVICES_API_KEY[serviceName] && (!apiKeyLS || apiKeyLS === '')) {
17431
+ throw new MissingApiKeyError(SERVICES_API_KEY[serviceName])
17432
+ }
17433
+
17434
+ // If proxy mode is enabled AND proxy URL exists for this service
17435
+ if ((isProxyModeEnabledValue || !apiKeyLS || apiKeyLS === '') && proxyConfig) {
17436
+ // Remove specified parameters from the target URL
17437
+ const cleanedUrl = removeUrlParams(url, proxyConfig.removeParams);
17438
+
17439
+ return {
17440
+ URL: proxyConfig.url,
17441
+ HEADERS: {
17442
+ 'target-url': cleanedUrl,
17443
+ method: 'GET',
17444
+ 'Content-Type': 'application/json'
17445
+ }
17446
+ };
17447
+ }
17448
+
17449
+ return {
17450
+ URL: url,
17451
+ HEADERS: {
17452
+ ...headers,
17453
+ }
17454
+ };
17455
+ }
17456
+
17457
+ const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
17458
+ if (!timestamp || !chain) return
17459
+ const chainId = CHAIN_ID_MAP[chain];
17460
+ const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
17461
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
17462
+ const res = await fetch(finalUrl, {
17463
+ method: 'GET',
17464
+ headers: HEADERS,
17465
+ });
17466
+ const json = await res.json();
17467
+ return parseInt(json.result);
17468
+
17469
+ };
17470
+
17471
+ var fromTimestampToBlock = {
17472
+ fromTimeStampToBlock
17473
+ };
17474
+
17475
+ function toTimestamp(dateStr) {
17476
+ // Expecting format: "DD/MM/YYYY"
17477
+ const [day, month, year] = dateStr.split("/").map(Number);
17478
+ const date = new Date(year, month - 1, day);
17479
+ return Math.floor(date.getTime() / 1000); // Unix timestamp in seconds
17480
+ }
17481
+
17482
+ /* global window */
17483
+
17484
+
17485
+
17486
+ async function EOA() {
17487
+ try {
17488
+ const [addresses, category, chains, startTime, endTime, page = 1, offset = 10] =
17489
+ argsToArray(arguments);
17490
+ validateParams(eoaParamsSchema, { addresses, category, chains, startTime, endTime, page, offset });
17491
+
17492
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
17493
+
17494
+ const INPUTS = addresses.split(',').map(s => s.trim()).filter(Boolean);
17495
+ const CHAINS = chains.split(',').map(s => s.trim()).filter(Boolean);
17496
+
17497
+ const ADDRESS_MAP = {};
17498
+ for (const inp of INPUTS) {
17499
+ if (isAddress$1.isAddress(inp)) {
17500
+ ADDRESS_MAP[inp.toLowerCase()] = null;
17501
+ } else {
17502
+ const _address = await fromEnsNameToAddress$1.validateAndGetAddress(inp);
17503
+ ADDRESS_MAP[_address.toLowerCase()] = _address;
17504
+ }
17505
+ }
17506
+ const ADDRS = Object.keys(ADDRESS_MAP);
17507
+ const out = [];
17508
+
17509
+ async function fetchJSON(url) {
17510
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
17511
+ const res = await fetch(finalUrl, {
17512
+ method: 'GET',
17513
+ headers: HEADERS,
17514
+ });
17515
+ if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Etherscan, res.status)
17516
+ const json = await res.json();
17517
+
17518
+ if (typeof json.result === 'string') {
17519
+ if (json.result.includes('Invalid API Key')) throw new InvalidApiKeyError(SERVICES_API_KEY.Etherscan)
17520
+ if (json.result.includes('Max rate limit reached')) throw new RateLimitError(SERVICES_API_KEY.Etherscan)
17521
+ }
17522
+ return json.result
17523
+ }
17524
+
17525
+
17526
+ for (const chain of CHAINS) {
17527
+ const chainId = CHAIN_ID_MAP[chain];
17528
+ if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
17529
+
17530
+ if (category === 'balance') {
17531
+ // chunk 20
17532
+ for (let i = 0; i < ADDRS.length; i += 20) {
17533
+ const slice = ADDRS.slice(i, i + 20).join(',');
17534
+ const url =
17535
+ `https://api.etherscan.io/v2/api?chainid=${chainId}` +
17536
+ `&module=account&action=addresstokenbalance&address=${slice}` +
17537
+ `&page=${page}&offset=${offset}&apikey=${apiKey}`;
17538
+ const data = await fetchJSON(url);
17539
+ if (!Array.isArray(data)) return data
17540
+ data.forEach((item, idx) => out.push({ chain, address: ADDRS[i + idx], name: ADDRESS_MAP[ADDRS[i + idx]], ...item }));
17541
+ }
17542
+ } else {
17543
+ // txns
17544
+ const sb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startTime), chain, apiKey);
17545
+ const eb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endTime), chain, apiKey);
17546
+ if (!sb) throw new ValidationError(`Invalid startTime: ${startTime}`)
17547
+ if (!eb) throw new ValidationError(`Invalid endTime: ${endTime}`)
17548
+ for (const addr of ADDRS) {
17549
+ const url =
17550
+ `https://api.etherscan.io/v2/api?chainid=${chainId}` +
17551
+ `&module=account&action=tokentx&address=${addr}` +
17552
+ `&startblock=${sb}&endblock=${eb}` +
17553
+ `&page=${page}&offset=${offset}&sort=asc&apikey=${apiKey}`;
17554
+ const data = await fetchJSON(url);
17555
+ if (!Array.isArray(data)) return data
17556
+ data.forEach(item => out.push({ chain, address: addr, name: ADDRESS_MAP[addr], ...item }));
17557
+ }
17558
+ }
17559
+ }
17560
+ return out
17561
+ } catch (err) {
17562
+ return errorMessageHandler(err, 'EOA')
17563
+ }
17564
+ }
17565
+
17566
+ const gasSchema$1 = objectType({
17567
+ type: literalType('gas'),
17568
+ startDate: dateOrTimestamp.optional(),
17569
+ endDate: dateOrTimestamp.optional(),
17570
+ page: numberType().int().nonnegative().default(1),
17571
+ limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17572
+ });
17573
+
17574
+ const txnSchema$1 = objectType({
17575
+ type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17576
+ address: stringType().nonempty(),
17577
+ startDate: dateOrTimestamp.optional(),
17578
+ endDate: dateOrTimestamp.optional(),
17579
+ page: numberType().int().nonnegative().default(1),
17580
+ limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17581
+ });
17582
+
17583
+ const baseParamsSchema = discriminatedUnionType('type', [gasSchema$1, txnSchema$1]);
17584
+
17585
+ async function handleScanRequest({
17586
+ type,
17587
+ address,
17588
+ startDate,
17589
+ endDate,
17590
+ page = 1,
17591
+ offset = 10,
17592
+ apiKey,
17593
+ functionName,
17594
+ chainId,
17595
+ network
17596
+ }) {
17597
+ const API_INFO_MAP = {
17598
+ BASE: { url: 'https://api.basescan.org/api', apiKeyName: SERVICES_API_KEY.Basescan },
17599
+ ETHERSCAN: { url: 'https://api.etherscan.io/v2/api', apiKeyName: SERVICES_API_KEY.Etherscan },
17600
+ GNOSIS: { url: 'https://api.gnosisscan.io/api', apiKeyName: SERVICES_API_KEY.Gnosisscan }
17601
+ };
17602
+
17603
+ if (type !== 'gas') {
17604
+ address = await fromEnsNameToAddress$1.validateAndGetAddress(address);
17605
+ }
17606
+
17607
+ const apiInfo = API_INFO_MAP[functionName];
17608
+ const baseUrl = apiInfo?.url;
17609
+ if (!baseUrl) throw new ValidationError(`Api not found for: ${functionName}`)
17610
+
17611
+
17612
+ const ACTION_MAP = {
17613
+ 'all-txns': 'txlist',
17614
+ 'token-txns': 'tokentx',
17615
+ 'nft-txns': 'tokennfttx',
17616
+ 'gas': 'gasoracle'
17617
+ };
17618
+
17619
+ const action = ACTION_MAP[type];
17620
+ if (!action) throw new ValidationError(`Invalid type: ${type}`)
17621
+
17622
+ const module = action === 'gasoracle' ? 'gastracker' : 'account';
17623
+ let url = `${baseUrl}?chainid=${chainId}&module=${module}&action=${action}&apikey=${apiKey}`;
17624
+
17625
+ if (['all-txns', 'token-txns', 'nft-txns'].includes(type)) {
17626
+ url += `&address=${address}&startblock=0&endblock=99999999&sort=asc`;
17627
+
17628
+ if (!isNaN(startDate) && !isNaN(endDate)) {
17629
+ const [startBlock, endBlock] = await Promise.all([
17630
+ fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startDate), network, apiKey),
17631
+ fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endDate), network, apiKey)
17632
+ ]);
17633
+ url += `&startblock=${startBlock || '0'}&endblock=${endBlock || '99999999'}`;
17634
+ }
17635
+ url += `&page=${page}&offset=${offset}`;
17636
+ }
17637
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({url, serviceName: apiInfo.apiKeyName, headers: {}});
17638
+ const res = await fetch(finalUrl, {
17639
+ method: 'GET',
17640
+ headers: HEADERS,
17641
+ });
17642
+ if (!res.ok) {
17643
+ throw new NetworkError(apiInfo.apiKeyName, res.status)
17644
+ }
17645
+ const json = await res.json();
17646
+
17647
+ if (typeof json.result === 'string') {
17648
+ if (json.result.includes('Invalid API Key')){
17649
+ throw new InvalidApiKeyError(apiInfo.apiKeyName)
17650
+ }
17651
+
17652
+ if (json.result.includes('Max rate limit reached'))
17653
+ throw new RateLimitError(apiInfo.apiKeyName)
17654
+ }
17655
+
17656
+ return type === 'gas' && !Array.isArray(json.result) ? [json.result] : json.result
17657
+ }
17658
+
17659
+ /* global window */
17660
+
17661
+
17662
+ async function BASE() {
17663
+ try {
17664
+ const [type, address, startDate, endDate, page, limit] = argsToArray(arguments);
17665
+ validateParams(baseParamsSchema, { type, address, startDate, endDate, page, limit });
17666
+ const API_KEY = window.localStorage.getItem(SERVICES_API_KEY.Basescan);
17667
+
17668
+ return await handleScanRequest({
17669
+ type,
17670
+ address,
17671
+ startDate,
17672
+ endDate,
17673
+ page,
17674
+ offset: limit,
17675
+ apiKey: API_KEY,
17676
+ functionName: 'BASE',
17677
+ chainId: CHAIN_ID_MAP.base,
17678
+ network: 'base'
17679
+ })
17680
+ } catch (error) {
17681
+ return errorMessageHandler(error, 'BASE')
17682
+ }
17683
+ }
17684
+
17586
17685
  const blockscoutParamsSchema = objectType({
17587
17686
  address: stringType().nonempty(),
17588
17687
  type: enumType(['stat', 'txns', 'tokens']),
@@ -17593,44 +17692,78 @@ const blockscoutParamsSchema = objectType({
17593
17692
  offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17594
17693
  });
17595
17694
 
17596
- const gasSchema$1 = objectType({
17597
- type: literalType('gas'),
17598
- startDate: dateOrTimestamp.optional(),
17599
- endDate: dateOrTimestamp.optional(),
17600
- page: numberType().int().nonnegative().default(1),
17601
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17602
- });
17695
+ async function BLOCKSCOUT() {
17696
+ try {
17697
+ const [
17698
+ address,
17699
+ type,
17700
+ chain = 'ethereum',
17701
+ startTimestamp,
17702
+ endTimestamp,
17703
+ page,
17704
+ offset,
17705
+ ] = argsToArray(arguments);
17603
17706
 
17604
- const txnSchema$1 = objectType({
17605
- type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17606
- address: stringType().nonempty(),
17607
- startDate: dateOrTimestamp.optional(),
17608
- endDate: dateOrTimestamp.optional(),
17609
- page: numberType().int().nonnegative().default(1),
17610
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17611
- });
17707
+ validateParams(blockscoutParamsSchema, {
17708
+ address,
17709
+ type,
17710
+ chain,
17711
+ startTimestamp,
17712
+ endTimestamp,
17713
+ page,
17714
+ offset
17715
+ });
17612
17716
 
17613
- const baseParamsSchema = discriminatedUnionType('type', [gasSchema$1, txnSchema$1]);
17717
+ const startTs =
17718
+ startTimestamp ?? Math.floor((Date.now() - 30 * 24 * 3600 * 1000) / 1000);
17719
+ const endTs = endTimestamp;
17614
17720
 
17615
- const gasSchema = objectType({
17616
- type: literalType('gas'),
17617
- startDate: dateOrTimestamp.optional(),
17618
- endDate: dateOrTimestamp.optional(),
17619
- page: numberType().int().nonnegative().default(1),
17620
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17621
- });
17721
+ const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
17622
17722
 
17623
- const txnSchema = objectType({
17624
- type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17625
- address: stringType().nonempty(),
17626
- startDate: dateOrTimestamp.optional(),
17627
- endDate: dateOrTimestamp.optional(),
17628
- chain: enumType(['ethereum','base','gnosis']),
17629
- page: numberType().int().nonnegative().default(1),
17630
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17631
- });
17723
+ const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
17632
17724
 
17633
- const etherscanParamsSchema = discriminatedUnionType('type', [gasSchema, txnSchema]);
17725
+ let requestUrl;
17726
+ switch (type) {
17727
+ case 'stat':
17728
+ requestUrl = `${hostname}/api/v2/addresses/${resolvedAddress}/counters`;
17729
+ break
17730
+ case 'txns':
17731
+ requestUrl =
17732
+ `${hostname}/api?module=account&action=txlist` +
17733
+ `&address=${resolvedAddress}` +
17734
+ `&start_timestamp=${startTs}` +
17735
+ `&end_timestamp=${endTs ?? ''}` +
17736
+ `&page=${page}` +
17737
+ `&offset=${offset}` +
17738
+ `&sort=asc`;
17739
+ break
17740
+ case 'tokens':
17741
+ requestUrl =
17742
+ `${hostname}/api?module=account&action=tokenlist` +
17743
+ `&address=${resolvedAddress}`;
17744
+ break
17745
+ }
17746
+
17747
+ const response = await fetch(requestUrl);
17748
+ if (!response.ok) {
17749
+ throw new NetworkError('BLOCKSCOUT', response.status)
17750
+ }
17751
+
17752
+ const json = await response.json();
17753
+
17754
+ // custom error conditions
17755
+ if (json?.result?.includes('Invalid parameter(s)')) {
17756
+ throw new ValidationError('Invalid parameters')
17757
+ }
17758
+ if (json?.result?.includes('Not found')) {
17759
+ throw new ValidationError('Address information not found')
17760
+ }
17761
+
17762
+ return type === 'stat' ? [json] : json.result
17763
+ } catch (err) {
17764
+ return errorMessageHandler(err, 'BLOCKSCOUT')
17765
+ }
17766
+ }
17634
17767
 
17635
17768
  const allowedValues = ['1h', '24h', '7d'];
17636
17769
  const param2Schema = stringType()
@@ -17671,123 +17804,67 @@ const coingeckoParamsSchema = discriminatedUnionType('category', [
17671
17804
  derivativesSchema,
17672
17805
  ]);
17673
17806
 
17674
- const baseSchema = objectType({
17675
- addresses: stringType().nonempty(),
17676
- category: enumType(['balance','txns']),
17677
- chains: preprocessType(
17678
- (val) => typeof val === 'string'
17679
- ? val.split(',').map(s => s.trim()).filter(Boolean)
17680
- : val,
17681
- arrayType(
17682
- enumType(['ethereum','gnosis','base'])
17683
- ).nonempty()
17684
- ),
17685
- startTime: dateOrTimestamp.optional(),
17686
- endTime: dateOrTimestamp.optional(),
17687
- page: numberType().int().nonnegative().default(1),
17688
- offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17689
- });
17690
-
17691
- const eoaParamsSchema = preprocessType(
17692
- (val) => {
17693
- const obj = { ...(val || {}) };
17694
- // if balance, ignore startTime/endTime
17695
- if (obj.category === 'balance') {
17696
- delete obj.startTime;
17697
- delete obj.endTime;
17698
- }
17699
- return obj
17700
- },
17701
- baseSchema.refine(data => {
17702
- // for txns, startTime and endTime are required
17703
- if (data.category === 'txns') {
17704
- return data.startTime !== undefined && data.endTime !== undefined
17705
- }
17706
- return true
17707
- }, {
17708
- message: 'startTime and endTime required for txns',
17709
- path: ['startTime'],
17710
- })
17711
- );
17712
-
17713
- const safeParamsSchema = objectType({
17714
- address: stringType().nonempty(),
17715
- utility: literalType('txns'),
17716
- chain: enumType(['ethereum','gnosis']),
17717
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17718
- offset: numberType().int().nonnegative().default(0),
17719
- });
17720
-
17721
- const categories = ['protocols','yields','dex','fees'];
17722
- const defillamaParamsSchema = objectType({
17723
- category: enumType(categories)
17724
- });
17725
-
17726
- const CATEGORY_URLS = {
17727
- protocols: 'https://api.llama.fi/protocols',
17728
- yields: 'https://yields.llama.fi/pools',
17729
- dex: 'https://api.llama.fi/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true',
17730
- fees: 'https://api.llama.fi/overview/fees?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true&dataType=dailyFees'
17731
- };
17732
-
17733
- const uniswapParamsSchema = objectType({
17734
- graphType: enumType(['v3','v3-raw']),
17735
- category: enumType(['tokens','markets']),
17736
- param1: stringType().nonempty(),
17737
- param2: stringType().optional(),
17738
- });
17739
-
17740
- const aaveParamsSchema = objectType({
17741
- graphType: enumType(['v2','v2-raw']),
17742
- category: enumType(['tokens','markets']),
17743
- param1: stringType().nonempty(),
17744
- param2: stringType().optional(),
17745
- });
17746
-
17747
17807
  /* global window */
17748
17808
 
17749
17809
 
17750
17810
 
17751
- async function FIREFLY() {
17811
+ async function COINGECKO() {
17752
17812
  try {
17753
- const [platform, contentType, identifier, start = 0, end = 10] = argsToArray(arguments);
17754
-
17755
- validateParams(fireflyParamsSchema, {
17756
- platform,
17757
- contentType,
17758
- identifier,
17759
- start,
17760
- end,
17761
- });
17813
+ const [category, param1, param2] = argsToArray(arguments);
17814
+ validateParams(coingeckoParamsSchema, { category, param1, param2 });
17762
17815
 
17763
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Firefly);
17816
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
17764
17817
 
17765
- const url = new URL('https://openapi.firefly.land/v1/fileverse/fetch');
17766
- url.searchParams
17767
- .set('query',
17768
- identifier
17769
- .split(',')
17770
- .map(s => s.trim())
17771
- .filter(Boolean)
17772
- .join(',')
17773
- );
17774
- url.searchParams.set('type', fireFlyPlaformType[platform][contentType]);
17775
- url.searchParams.set('start', String(start));
17776
- url.searchParams.set('end', String(end));
17818
+ const headers = {
17819
+ accept: 'application/json',
17820
+ 'x-cg-demo-api-key': apiKey,
17821
+ };
17822
+ let url = '';
17823
+ switch (category?.toLowerCase?.()) {
17824
+ case 'price': {
17825
+ const vs = param2 || 'usd';
17826
+ url = `https://api.coingecko.com/api/v3/simple/price?vs_currencies=${vs}&symbols=${param1}`;
17827
+ break
17828
+ }
17829
+ case 'market': {
17830
+ const map = { all: '', base: 'base-ecosystem', meme: 'meme-token', aiagents: 'ai-agents', bitcoin: 'bitcoin-ecosystem', ethereum: 'ethereum-ecosystem', hyperliquid: 'hyperliquid-ecosystem', pump: 'pump-ecosystem', solana: 'solana-ecosystem' };
17831
+ const _category = map[param1] || '';
17832
+ const trend = param2 ? `&price_change_percentage=${param2}` : '';
17833
+ url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category ? `&category=${_category}` : ''}${trend}`;
17834
+ break
17835
+ }
17836
+ case 'stablecoins': {
17837
+ const _category = param1 === 'all' ? 'stablecoins' : param1;
17838
+ const trend = param2 ? `&price_change_percentage=${param2}` : '';
17839
+ url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=${_category}&order=market_cap_desc&page=1&per_page=100${trend}`;
17840
+ break
17841
+ }
17842
+ case 'derivatives': {
17843
+ url = (!param1 || param1 === 'all')
17844
+ ? 'https://api.coingecko.com/api/v3/derivatives'
17845
+ : `https://api.coingecko.com/api/v3/derivatives/exchanges/${param1}?include_tickers=all`;
17846
+ break
17847
+ }
17848
+ }
17849
+ const {URL: finalUrl, HEADERS} = getUrlAndHeaders({url, serviceName: 'Coingecko', headers});
17777
17850
 
17778
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17779
- const response = await fetch(finalUrl, {
17780
- method: 'GET',
17781
- headers: HEADERS,
17782
- });
17783
- if (!response.ok) {
17784
- throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
17851
+ const res = await fetch(finalUrl, { headers: HEADERS });
17852
+ const json = await res.json();
17853
+ if (!res.ok) {
17854
+ const msg = json?.status?.error_message || '';
17855
+ if (msg.includes('API Key Missing')) throw new InvalidApiKeyError(SERVICES_API_KEY.Coingecko)
17856
+ throw new NetworkError(SERVICES_API_KEY.Coingecko, res.status)
17785
17857
  }
17786
17858
 
17787
- const { data } = await response.json();
17788
- if (!Array.isArray(data)) {
17789
- return []
17859
+ if (category === 'price') {
17860
+ const out = {};
17861
+ for (const [token, prices] of Object.entries(json))
17862
+ for (const [cur, val] of Object.entries(prices))
17863
+ out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
17864
+ return [out]
17790
17865
  }
17866
+
17867
+ const data = Array.isArray(json) ? json : [json];
17791
17868
  return data.map(item => {
17792
17869
  const flat = {};
17793
17870
  for (const [key, value] of Object.entries(item)) {
@@ -17795,78 +17872,133 @@ async function FIREFLY() {
17795
17872
  flat[key] = value;
17796
17873
  }
17797
17874
  }
17798
- flat.platform = platform;
17799
17875
  return flat
17800
17876
  })
17801
-
17802
17877
  } catch (err) {
17803
- return errorMessageHandler(err, 'FIREFLY')
17878
+ return errorMessageHandler(err, 'COINGECKO')
17804
17879
  }
17805
17880
  }
17806
17881
 
17882
+ const categories = ['protocols','yields','dex','fees'];
17883
+ const defillamaParamsSchema = objectType({
17884
+ category: enumType(categories)
17885
+ });
17886
+
17887
+ const CATEGORY_URLS = {
17888
+ protocols: 'https://api.llama.fi/protocols',
17889
+ yields: 'https://yields.llama.fi/pools',
17890
+ dex: 'https://api.llama.fi/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true',
17891
+ fees: 'https://api.llama.fi/overview/fees?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true&dataType=dailyFees'
17892
+ };
17807
17893
 
17808
- async function LENS() {
17894
+ async function DEFILLAMA() {
17809
17895
  try {
17810
- const [contentType, identifier, start = 0, end = 10] =
17811
- argsToArray(arguments);
17896
+ const [category] = argsToArray(arguments);
17897
+ validateParams(defillamaParamsSchema, { category });
17898
+ const url = CATEGORY_URLS[category];
17899
+ if (!url) throw new ValidationError(`Invalid category: ${category}`)
17900
+ const res = await fetch(url);
17901
+ if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Defillama, res.status)
17902
+ let json = await res.json();
17812
17903
 
17813
- validateParams(lensParamsSchema, {
17814
- contentType,
17815
- identifier,
17816
- start,
17817
- end,
17818
- });
17904
+ switch (category) {
17905
+ case 'protocols':
17906
+ json = Array.isArray(json) ? json.slice(0, 500) : [];
17907
+ break
17908
+ case 'yields':
17909
+ json = Array.isArray(json.data) ? json.data.slice(0, 500) : [];
17910
+ break
17911
+ case 'dex':
17912
+ case 'fees':
17913
+ json = Array.isArray(json.protocols) ? json.protocols.slice(0, 500) : [];
17914
+ break
17915
+ }
17819
17916
 
17820
- const apiKey = window.localStorage.getItem(
17821
- SERVICES_API_KEY.Firefly
17822
- );
17917
+ return (Array.isArray(json) ? json : [json]).map(item => {
17918
+ const out = {};
17919
+ for (const [k, v] of Object.entries(item)) {
17920
+ if (v === null || typeof v !== 'object') out[k] = v;
17921
+ }
17922
+ return out
17923
+ })
17924
+ } catch (err) {
17925
+ return errorMessageHandler(err, 'DEFILLAMA')
17926
+ }
17927
+ }
17823
17928
 
17824
- const url = new URL(
17825
- 'https://openapi.firefly.land/v1/fileverse/fetch'
17826
- );
17827
- url.searchParams.set(
17828
- 'query',
17829
- identifier
17830
- .split(',')
17831
- .map((s) => s.trim())
17832
- .filter(Boolean)
17833
- .join(',')
17834
- );
17835
- const typeMap = {
17836
- posts: 'lensid',
17837
- replies: 'lenspostid',
17838
- };
17839
- url.searchParams.set('type', typeMap[contentType]);
17840
- url.searchParams.set('start', String(start));
17841
- url.searchParams.set('end', String(end));
17929
+ const gasSchema = objectType({
17930
+ type: literalType('gas'),
17931
+ startDate: dateOrTimestamp.optional(),
17932
+ endDate: dateOrTimestamp.optional(),
17933
+ page: numberType().int().nonnegative().default(1),
17934
+ limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17935
+ });
17842
17936
 
17843
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17937
+ const txnSchema = objectType({
17938
+ type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17939
+ address: stringType().nonempty(),
17940
+ startDate: dateOrTimestamp.optional(),
17941
+ endDate: dateOrTimestamp.optional(),
17942
+ chain: enumType(['ethereum','base','gnosis']),
17943
+ page: numberType().int().nonnegative().default(1),
17944
+ limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17945
+ });
17844
17946
 
17845
- const response = await fetch(finalUrl, {
17846
- method: 'GET',
17847
- headers: HEADERS,
17848
- });
17849
- if (!response.ok) {
17850
- throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
17851
- }
17947
+ const etherscanParamsSchema = discriminatedUnionType('type', [gasSchema, txnSchema]);
17852
17948
 
17853
- const { data } = await response.json();
17854
- if (!Array.isArray(data)) return []
17949
+ /* global window */
17855
17950
 
17856
- return data.map((item) => {
17857
- const flat = {};
17858
- for (const [key, value] of Object.entries(item)) {
17859
- if (value == null || typeof value !== 'object') {
17860
- flat[key] = value;
17861
- }
17862
- }
17863
- flat.platform = 'lens';
17864
- return flat
17951
+
17952
+
17953
+
17954
+
17955
+
17956
+ async function ETHERSCAN() {
17957
+ try {
17958
+ const [type, chain, address, startDate, endDate, page = 1, limit = 10] =
17959
+ argsToArray(arguments);
17960
+
17961
+
17962
+ validateParams(etherscanParamsSchema, { type, chain, address, startDate, endDate, page, limit });
17963
+
17964
+ const chainId = CHAIN_ID_MAP[chain];
17965
+ if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
17966
+
17967
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
17968
+
17969
+ return await handleScanRequest({
17970
+ type,
17971
+ address,
17972
+ startDate,
17973
+ endDate,
17974
+ page,
17975
+ offset: limit,
17976
+ apiKey,
17977
+ functionName: 'ETHERSCAN',
17978
+ chainId,
17979
+ network: chain,
17865
17980
  })
17866
17981
  } catch (err) {
17867
- return errorMessageHandler(err, 'LENS')
17982
+ return errorMessageHandler(err, 'ETHERSCAN')
17868
17983
  }
17869
17984
  }
17985
+
17986
+ const farcasterParamsSchema = objectType({
17987
+ contentType: enumType(['posts', 'replies', 'channels']),
17988
+ identifier: stringType().nonempty(),
17989
+ start: numberType().int().nonnegative().default(0),
17990
+ end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17991
+ });
17992
+
17993
+ /* global window */
17994
+
17995
+
17996
+
17997
+
17998
+
17999
+
18000
+
18001
+
17870
18002
  async function FARCASTER() {
17871
18003
  try {
17872
18004
  const [contentType, identifier, start = 0, end = 10] =
@@ -17933,102 +18065,108 @@ async function FARCASTER() {
17933
18065
  }
17934
18066
  }
17935
18067
 
18068
+ const farcasterSchema = objectType({
18069
+ platform: literalType('farcaster'),
18070
+ contentType: enumType(['posts', 'replies', 'channels']),
18071
+ identifier: stringType().nonempty(),
18072
+ start: numberType().int().nonnegative().default(0),
18073
+ end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
18074
+ });
18075
+
18076
+ const lensSchema = objectType({
18077
+ platform: literalType('lens'),
18078
+ contentType: enumType(['posts', 'replies']),
18079
+ identifier: stringType().nonempty(),
18080
+ start: numberType().int().nonnegative().default(0),
18081
+ end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
18082
+ });
17936
18083
 
17937
- async function BLOCKSCOUT() {
17938
- try {
17939
- const [
17940
- address,
17941
- type,
17942
- chain = 'ethereum',
17943
- startTimestamp,
17944
- endTimestamp,
17945
- page,
17946
- offset,
17947
- ] = argsToArray(arguments);
18084
+ const fireflyParamsSchema = discriminatedUnionType('platform', [
18085
+ farcasterSchema,
18086
+ lensSchema,
18087
+ ]);
18088
+ const fireFlyPlaformType = {
18089
+ farcaster: {
18090
+ posts: 'farcasterid',
18091
+ replies: 'farcasterpostid',
18092
+ channels: 'farcasterchannels'
18093
+ },
18094
+ lens: {
18095
+ posts: 'lensid',
18096
+ replies: 'lenspostid'
18097
+ }
18098
+ };
17948
18099
 
17949
- validateParams(blockscoutParamsSchema, {
17950
- address,
17951
- type,
17952
- chain,
17953
- startTimestamp,
17954
- endTimestamp,
17955
- page,
17956
- offset
17957
- });
18100
+ /* global window */
17958
18101
 
17959
- const startTs =
17960
- startTimestamp ?? Math.floor((Date.now() - 30 * 24 * 3600 * 1000) / 1000);
17961
- const endTs = endTimestamp;
17962
18102
 
17963
- const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
17964
18103
 
17965
- const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
17966
18104
 
17967
- let requestUrl;
17968
- switch (type) {
17969
- case 'stat':
17970
- requestUrl = `${hostname}/api/v2/addresses/${resolvedAddress}/counters`;
17971
- break
17972
- case 'txns':
17973
- requestUrl =
17974
- `${hostname}/api?module=account&action=txlist` +
17975
- `&address=${resolvedAddress}` +
17976
- `&start_timestamp=${startTs}` +
17977
- `&end_timestamp=${endTs ?? ''}` +
17978
- `&page=${page}` +
17979
- `&offset=${offset}` +
17980
- `&sort=asc`;
17981
- break
17982
- case 'tokens':
17983
- requestUrl =
17984
- `${hostname}/api?module=account&action=tokenlist` +
17985
- `&address=${resolvedAddress}`;
17986
- break
17987
- }
17988
18105
 
17989
- const response = await fetch(requestUrl);
18106
+
18107
+ async function FIREFLY() {
18108
+ try {
18109
+ const [platform, contentType, identifier, start = 0, end = 10] = argsToArray(arguments);
18110
+
18111
+ validateParams(fireflyParamsSchema, {
18112
+ platform,
18113
+ contentType,
18114
+ identifier,
18115
+ start,
18116
+ end,
18117
+ });
18118
+
18119
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Firefly);
18120
+
18121
+ const url = new URL('https://openapi.firefly.land/v1/fileverse/fetch');
18122
+ url.searchParams
18123
+ .set('query',
18124
+ identifier
18125
+ .split(',')
18126
+ .map(s => s.trim())
18127
+ .filter(Boolean)
18128
+ .join(',')
18129
+ );
18130
+ url.searchParams.set('type', fireFlyPlaformType[platform][contentType]);
18131
+ url.searchParams.set('start', String(start));
18132
+ url.searchParams.set('end', String(end));
18133
+
18134
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
18135
+ const response = await fetch(finalUrl, {
18136
+ method: 'GET',
18137
+ headers: HEADERS,
18138
+ });
17990
18139
  if (!response.ok) {
17991
- throw new NetworkError('BLOCKSCOUT', response.status)
18140
+ throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
17992
18141
  }
17993
18142
 
17994
- const json = await response.json();
17995
-
17996
- // custom error conditions
17997
- if (json?.result?.includes('Invalid parameter(s)')) {
17998
- throw new ValidationError('Invalid parameters')
17999
- }
18000
- if (json?.result?.includes('Not found')) {
18001
- throw new ValidationError('Address information not found')
18143
+ const { data } = await response.json();
18144
+ if (!Array.isArray(data)) {
18145
+ return []
18002
18146
  }
18147
+ return data.map(item => {
18148
+ const flat = {};
18149
+ for (const [key, value] of Object.entries(item)) {
18150
+ if (typeof value !== 'object' || value === null) {
18151
+ flat[key] = value;
18152
+ }
18153
+ }
18154
+ flat.platform = platform;
18155
+ return flat
18156
+ })
18003
18157
 
18004
- return type === 'stat' ? [json] : json.result
18005
18158
  } catch (err) {
18006
- return errorMessageHandler(err, 'BLOCKSCOUT')
18159
+ return errorMessageHandler(err, 'FIREFLY')
18007
18160
  }
18008
18161
  }
18009
18162
 
18010
- async function BASE() {
18011
- try {
18012
- const [type, address, startDate, endDate, page, limit] = argsToArray(arguments);
18013
- validateParams(baseParamsSchema, { type, address, startDate, endDate, page, limit });
18014
- const API_KEY = window.localStorage.getItem(SERVICES_API_KEY.Basescan);
18163
+ /* global window */
18164
+
18165
+
18166
+
18167
+
18168
+
18015
18169
 
18016
- return await handleScanRequest({
18017
- type,
18018
- address,
18019
- startDate,
18020
- endDate,
18021
- page,
18022
- offset: limit,
18023
- apiKey: API_KEY,
18024
- functionName: 'BASE',
18025
- chainId: CHAIN_ID_MAP.base,
18026
- network: 'base'
18027
- })
18028
- } catch (error) {
18029
- return errorMessageHandler(error, 'BASE')
18030
- }
18031
- }
18032
18170
  async function GNOSIS() {
18033
18171
  try {
18034
18172
  const [type, address, startDate, endDate, page = 1, limit = 10] =
@@ -18065,306 +18203,178 @@ async function GNOSIS() {
18065
18203
  }
18066
18204
  }
18067
18205
 
18068
- async function NEYNAR() {
18069
- try {
18070
- const neynarParamsSchema = objectType({
18071
- username: stringType().nonempty()
18072
- });
18073
-
18074
- const [username] = argsToArray(arguments);
18075
-
18076
- validateParams(neynarParamsSchema, { username });
18077
-
18078
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Neynar);
18079
-
18080
- const fid = await fromUsernameToFid$1.fromUsernameToFid(username, apiKey);
18081
- if (!fid) throw new ValidationError(`Invalid username: ${username}`)
18082
-
18083
- const url = `https://api.neynar.com/v2/farcaster/followers?fid=${fid}`;
18084
-
18085
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
18086
- url: url.toString(), serviceName: 'Neynar',
18087
- headers: {
18088
- 'x-api-key': apiKey,
18089
- 'x-neynar-experimental': 'false'
18090
- }
18091
-
18092
- });
18093
-
18094
- const response = await fetch(finalUrl, {
18095
- method: 'GET',
18096
- headers: HEADERS,
18097
- });
18098
- if (!response.ok) {
18099
- throw new NetworkError(SERVICES_API_KEY.Neynar, response.status)
18100
- }
18101
-
18102
- const json = await response.json();
18103
- const users = json?.users || [];
18104
- if (!users.length) return []
18105
-
18106
- return users.map(({ user }) => ({
18107
- username: user.username,
18108
- custody_address: user.custody_address,
18109
- follower_count: user.follower_count,
18110
- country: user.profile?.location?.address?.country || '',
18111
- city: user.profile?.location?.address?.city || '',
18112
- }))
18113
- } catch (err) {
18114
- return errorMessageHandler(err, 'NEYNAR')
18115
- }
18116
- }
18206
+ const lensParamsSchema = objectType({
18207
+ contentType: enumType(['posts', 'replies']),
18208
+ identifier: stringType().nonempty(),
18209
+ start: numberType().int().nonnegative().default(0),
18210
+ end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
18211
+ });
18117
18212
 
18118
- // export async function GNOSISPAY({
18119
- // cardId,
18120
- // startDate,
18121
- // endDate,
18122
- // limit = 20,
18123
- // offset = 0,
18124
- // }) {
18125
- // const apiKeyKey = SERVICES_API_KEY.GnosisPay
18126
- // const API_KEY = window.localStorage.getItem(apiKeyKey);
18127
- // if (!API_KEY) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.MISSING_KEY}`;
18128
- // if (!cardId) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.INVALID_PARAM}`;
18129
- // if(limit > MAX_PAGE_LIMIT){
18130
- // return ERROR_MESSAGES_FLAG.MAX_PAGE_LIMIT
18131
- // }
18213
+ /* global window */
18132
18214
 
18133
- // const url = new URL(`https://api.gnosispay.com/cards/${cardId}/transactions`);
18134
- // url.searchParams.set('limit', limit.toString());
18135
- // url.searchParams.set('offset', offset.toString());
18136
18215
 
18137
- // if (!isNaN(toTimestamp(startDate))) {
18138
- // url.searchParams.set('startDate', new Date(startDate * 1000).toISOString());
18139
- // }
18140
18216
 
18141
- // if (!isNaN(toTimestamp(endDate))) {
18142
- // url.searchParams.set('endDate', new Date(endDate * 1000).toISOString());
18143
- // }
18144
18217
 
18145
- // try {
18146
- // const res = await fetch(url.toString(), {
18147
- // headers: {
18148
- // Authorization: `Bearer ${API_KEY}`,
18149
- // 'Content-Type': 'application/json',
18150
- // },
18151
- // });
18152
-
18153
- // if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
18154
-
18155
- // const json = await res.json();
18156
-
18157
- // if (!Array.isArray(json)) return [];
18158
-
18159
- // return json.map(tx => ({
18160
- // createdAt: tx.createdAt,
18161
- // clearedAt: tx.clearedAt,
18162
- // country: tx.country,
18163
- // merchant: tx.merchant,
18164
- // billingAmount: tx.billingAmount,
18165
- // billingCurrency: tx.billingCurrency,
18166
- // transactionAmount: tx.transactionAmount,
18167
- // transactionCurrency: tx.transactionCurrency,
18168
- // transactionType: tx.transactionType,
18169
- // kind: tx.kind,
18170
- // status: tx.status || null,
18171
- // mcc: tx.mcc,
18172
- // }));
18173
- // } catch (err) {
18174
- // console.error('GNOSISPAY_CARD_TXNS error:', err);
18175
- // return ERROR_MESSAGES_FLAG.DEFAULT;
18176
- // }
18177
- // }
18178
18218
 
18179
- async function ETHERSCAN() {
18219
+ async function LENS() {
18180
18220
  try {
18181
- const [type, chain, address, startDate, endDate, page = 1, limit = 10] =
18221
+ const [contentType, identifier, start = 0, end = 10] =
18182
18222
  argsToArray(arguments);
18183
18223
 
18224
+ validateParams(lensParamsSchema, {
18225
+ contentType,
18226
+ identifier,
18227
+ start,
18228
+ end,
18229
+ });
18184
18230
 
18185
- validateParams(etherscanParamsSchema, { type, chain, address, startDate, endDate, page, limit });
18186
-
18187
- const chainId = CHAIN_ID_MAP[chain];
18188
- if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
18189
-
18190
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
18191
-
18192
- return await handleScanRequest({
18193
- type,
18194
- address,
18195
- startDate,
18196
- endDate,
18197
- page,
18198
- offset: limit,
18199
- apiKey,
18200
- functionName: 'ETHERSCAN',
18201
- chainId,
18202
- network: chain,
18203
- })
18204
- } catch (err) {
18205
- return errorMessageHandler(err, 'ETHERSCAN')
18206
- }
18207
- }
18208
-
18209
-
18210
- async function COINGECKO() {
18211
- try {
18212
- const [category, param1, param2] = argsToArray(arguments);
18213
- validateParams(coingeckoParamsSchema, { category, param1, param2 });
18214
-
18215
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
18231
+ const apiKey = window.localStorage.getItem(
18232
+ SERVICES_API_KEY.Firefly
18233
+ );
18216
18234
 
18217
- const headers = {
18218
- accept: 'application/json',
18219
- 'x-cg-demo-api-key': apiKey,
18235
+ const url = new URL(
18236
+ 'https://openapi.firefly.land/v1/fileverse/fetch'
18237
+ );
18238
+ url.searchParams.set(
18239
+ 'query',
18240
+ identifier
18241
+ .split(',')
18242
+ .map((s) => s.trim())
18243
+ .filter(Boolean)
18244
+ .join(',')
18245
+ );
18246
+ const typeMap = {
18247
+ posts: 'lensid',
18248
+ replies: 'lenspostid',
18220
18249
  };
18221
- let url = '';
18222
- switch (category?.toLowerCase?.()) {
18223
- case 'price': {
18224
- const vs = param2 || 'usd';
18225
- url = `https://api.coingecko.com/api/v3/simple/price?vs_currencies=${vs}&symbols=${param1}`;
18226
- break
18227
- }
18228
- case 'market': {
18229
- const map = { all: '', base: 'base-ecosystem', meme: 'meme-token', aiagents: 'ai-agents', bitcoin: 'bitcoin-ecosystem', ethereum: 'ethereum-ecosystem', hyperliquid: 'hyperliquid-ecosystem', pump: 'pump-ecosystem', solana: 'solana-ecosystem' };
18230
- const _category = map[param1] || '';
18231
- const trend = param2 ? `&price_change_percentage=${param2}` : '';
18232
- url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category ? `&category=${_category}` : ''}${trend}`;
18233
- break
18234
- }
18235
- case 'stablecoins': {
18236
- const _category = param1 === 'all' ? 'stablecoins' : param1;
18237
- const trend = param2 ? `&price_change_percentage=${param2}` : '';
18238
- url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=${_category}&order=market_cap_desc&page=1&per_page=100${trend}`;
18239
- break
18240
- }
18241
- case 'derivatives': {
18242
- url = (!param1 || param1 === 'all')
18243
- ? 'https://api.coingecko.com/api/v3/derivatives'
18244
- : `https://api.coingecko.com/api/v3/derivatives/exchanges/${param1}?include_tickers=all`;
18245
- break
18246
- }
18247
- }
18248
- const {URL: finalUrl, HEADERS} = getUrlAndHeaders({url, serviceName: 'Coingecko', headers});
18250
+ url.searchParams.set('type', typeMap[contentType]);
18251
+ url.searchParams.set('start', String(start));
18252
+ url.searchParams.set('end', String(end));
18249
18253
 
18250
- const res = await fetch(finalUrl, { headers: HEADERS });
18251
- const json = await res.json();
18252
- if (!res.ok) {
18253
- const msg = json?.status?.error_message || '';
18254
- if (msg.includes('API Key Missing')) throw new InvalidApiKeyError(SERVICES_API_KEY.Coingecko)
18255
- throw new NetworkError(SERVICES_API_KEY.Coingecko, res.status)
18254
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
18255
+
18256
+ const response = await fetch(finalUrl, {
18257
+ method: 'GET',
18258
+ headers: HEADERS,
18259
+ });
18260
+ if (!response.ok) {
18261
+ throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
18256
18262
  }
18257
18263
 
18258
- if (category === 'price') {
18259
- const out = {};
18260
- for (const [token, prices] of Object.entries(json))
18261
- for (const [cur, val] of Object.entries(prices))
18262
- out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
18263
- return [out]
18264
- }
18264
+ const { data } = await response.json();
18265
+ if (!Array.isArray(data)) return []
18265
18266
 
18266
- const data = Array.isArray(json) ? json : [json];
18267
- return data.map(item => {
18267
+ return data.map((item) => {
18268
18268
  const flat = {};
18269
18269
  for (const [key, value] of Object.entries(item)) {
18270
- if (typeof value !== 'object' || value === null) {
18270
+ if (value == null || typeof value !== 'object') {
18271
18271
  flat[key] = value;
18272
18272
  }
18273
18273
  }
18274
+ flat.platform = 'lens';
18274
18275
  return flat
18275
18276
  })
18276
18277
  } catch (err) {
18277
- return errorMessageHandler(err, 'COINGECKO')
18278
+ return errorMessageHandler(err, 'LENS')
18278
18279
  }
18279
18280
  }
18280
18281
 
18281
- async function EOA() {
18282
- try {
18283
- const [addresses, category, chains, startTime, endTime, page = 1, offset = 10] =
18284
- argsToArray(arguments);
18285
- validateParams(eoaParamsSchema, { addresses, category, chains, startTime, endTime, page, offset });
18282
+ const neynarParamsSchema = objectType({
18283
+ username: stringType().nonempty()
18284
+ });
18286
18285
 
18287
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
18286
+ const fromUsernameToFid = async (username, apiKey) => {
18287
+ if (!username) return null
18288
+ const url = `https://api.neynar.com/v2/farcaster/user/search/?q=${username}&limit=5`;
18289
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
18290
+ url, serviceName: 'Neynar', headers: {
18291
+ 'x-api-key': apiKey,
18292
+ 'x-neynar-experimental': 'false'
18293
+ }
18294
+ });
18288
18295
 
18289
- const INPUTS = addresses.split(',').map(s => s.trim()).filter(Boolean);
18290
- const CHAINS = chains.split(',').map(s => s.trim()).filter(Boolean);
18296
+ const res = await fetch(finalUrl, {
18297
+ method: 'GET',
18298
+ headers: HEADERS,
18299
+ });
18300
+ const json = await res.json();
18301
+ const users = json.result ? json.result.users : [];
18302
+ const user = users.find(user => user.username === username);
18303
+ return user && user.fid || null;
18304
+ };
18305
+ var fromUsernameToFid$1 = {
18306
+ fromUsernameToFid
18307
+ };
18291
18308
 
18292
- const ADDRESS_MAP = {};
18293
- for (const inp of INPUTS) {
18294
- if (isAddress$1.isAddress(inp)) {
18295
- ADDRESS_MAP[inp.toLowerCase()] = null;
18296
- } else {
18297
- const _address = await fromEnsNameToAddress$1.validateAndGetAddress(inp);
18298
- ADDRESS_MAP[_address.toLowerCase()] = _address;
18299
- }
18300
- }
18301
- const ADDRS = Object.keys(ADDRESS_MAP);
18302
- const out = [];
18309
+ /* global window */
18303
18310
 
18304
- async function fetchJSON(url) {
18305
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
18306
- const res = await fetch(finalUrl, {
18307
- method: 'GET',
18308
- headers: HEADERS,
18309
- });
18310
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Etherscan, res.status)
18311
- const json = await res.json();
18312
18311
 
18313
- if (typeof json.result === 'string') {
18314
- if (json.result.includes('Invalid API Key')) throw new InvalidApiKeyError(SERVICES_API_KEY.Etherscan)
18315
- if (json.result.includes('Max rate limit reached')) throw new RateLimitError(SERVICES_API_KEY.Etherscan)
18316
- }
18317
- return json.result
18318
- }
18319
18312
 
18320
18313
 
18321
- for (const chain of CHAINS) {
18322
- const chainId = CHAIN_ID_MAP[chain];
18323
- if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
18324
18314
 
18325
- if (category === 'balance') {
18326
- // chunk 20
18327
- for (let i = 0; i < ADDRS.length; i += 20) {
18328
- const slice = ADDRS.slice(i, i + 20).join(',');
18329
- const url =
18330
- `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18331
- `&module=account&action=addresstokenbalance&address=${slice}` +
18332
- `&page=${page}&offset=${offset}&apikey=${apiKey}`;
18333
- const data = await fetchJSON(url);
18334
- if (!Array.isArray(data)) return data
18335
- data.forEach((item, idx) => out.push({ chain, address: ADDRS[i + idx], name: ADDRESS_MAP[ADDRS[i + idx]], ...item }));
18336
- }
18337
- } else {
18338
- // txns
18339
- const sb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startTime), chain, apiKey);
18340
- const eb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endTime), chain, apiKey);
18341
- if (!sb) throw new ValidationError(`Invalid startTime: ${startTime}`)
18342
- if (!eb) throw new ValidationError(`Invalid endTime: ${endTime}`)
18343
- for (const addr of ADDRS) {
18344
- const url =
18345
- `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18346
- `&module=account&action=tokentx&address=${addr}` +
18347
- `&startblock=${sb}&endblock=${eb}` +
18348
- `&page=${page}&offset=${offset}&sort=asc&apikey=${apiKey}`;
18349
- const data = await fetchJSON(url);
18350
- if (!Array.isArray(data)) return data
18351
- data.forEach(item => out.push({ chain, address: addr, name: ADDRESS_MAP[addr], ...item }));
18315
+
18316
+
18317
+ async function NEYNAR() {
18318
+ try {
18319
+
18320
+
18321
+ const [username] = argsToArray(arguments);
18322
+
18323
+ validateParams(neynarParamsSchema, { username });
18324
+
18325
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Neynar);
18326
+
18327
+ const fid = await fromUsernameToFid$1.fromUsernameToFid(username, apiKey);
18328
+ if (!fid) throw new ValidationError(`Invalid username: ${username}`)
18329
+
18330
+ const url = `https://api.neynar.com/v2/farcaster/followers?fid=${fid}`;
18331
+
18332
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
18333
+ url: url.toString(), serviceName: 'Neynar',
18334
+ headers: {
18335
+ 'x-api-key': apiKey,
18336
+ 'x-neynar-experimental': 'false'
18352
18337
  }
18353
- }
18338
+
18339
+ });
18340
+
18341
+ const response = await fetch(finalUrl, {
18342
+ method: 'GET',
18343
+ headers: HEADERS,
18344
+ });
18345
+ if (!response.ok) {
18346
+ throw new NetworkError(SERVICES_API_KEY.Neynar, response.status)
18354
18347
  }
18355
- return out
18348
+
18349
+ const json = await response.json();
18350
+ const users = json?.users || [];
18351
+ if (!users.length) return []
18352
+
18353
+ return users.map(({ user }) => ({
18354
+ username: user.username,
18355
+ custody_address: user.custody_address,
18356
+ follower_count: user.follower_count,
18357
+ country: user.profile?.location?.address?.country || '',
18358
+ city: user.profile?.location?.address?.city || '',
18359
+ }))
18356
18360
  } catch (err) {
18357
- return errorMessageHandler(err, 'EOA')
18361
+ return errorMessageHandler(err, 'NEYNAR')
18358
18362
  }
18359
18363
  }
18360
18364
 
18361
- async function FLVURL(token, vs_currencies) {
18362
- return new Promise((resolve) => {
18363
- setTimeout(() => {
18364
- resolve([{ Yoo: 'gotcha' }]);
18365
- }, 10000);
18366
- })
18367
- }
18365
+ const safeParamsSchema = objectType({
18366
+ address: stringType().nonempty(),
18367
+ utility: literalType('txns'),
18368
+ chain: enumType(['ethereum','gnosis']),
18369
+ limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
18370
+ offset: numberType().int().nonnegative().default(0),
18371
+ });
18372
+
18373
+ /* global window */
18374
+
18375
+
18376
+
18377
+
18368
18378
 
18369
18379
  async function SAFE() {
18370
18380
  try {
@@ -18401,42 +18411,12 @@ async function SAFE() {
18401
18411
  }
18402
18412
  }
18403
18413
 
18404
-
18405
- async function DEFILLAMA() {
18406
- try {
18407
- const [category] = argsToArray(arguments);
18408
- validateParams(defillamaParamsSchema, { category });
18409
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Defillama);
18410
- const url = CATEGORY_URLS[category];
18411
- if (!url) throw new ValidationError(`Invalid category: ${category}`)
18412
- const res = await fetch(url);
18413
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Defillama, res.status)
18414
- let json = await res.json();
18415
-
18416
- switch (category) {
18417
- case 'protocols':
18418
- json = Array.isArray(json) ? json.slice(0, 500) : [];
18419
- break
18420
- case 'yields':
18421
- json = Array.isArray(json.data) ? json.data.slice(0, 500) : [];
18422
- break
18423
- case 'dex':
18424
- case 'fees':
18425
- json = Array.isArray(json.protocols) ? json.protocols.slice(0, 500) : [];
18426
- break
18427
- }
18428
-
18429
- return (Array.isArray(json) ? json : [json]).map(item => {
18430
- const out = {};
18431
- for (const [k, v] of Object.entries(item)) {
18432
- if (v === null || typeof v !== 'object') out[k] = v;
18433
- }
18434
- return out
18435
- })
18436
- } catch (err) {
18437
- return errorMessageHandler(err, 'DEFILLAMA')
18438
- }
18439
- }
18414
+ const uniswapParamsSchema = objectType({
18415
+ graphType: enumType(['v3','v3-raw']),
18416
+ category: enumType(['tokens','markets']),
18417
+ param1: stringType().nonempty(),
18418
+ param2: stringType().optional(),
18419
+ });
18440
18420
 
18441
18421
  async function UNISWAP() {
18442
18422
  try {
@@ -18476,43 +18456,12 @@ async function UNISWAP() {
18476
18456
  }
18477
18457
  }
18478
18458
 
18479
-
18480
- async function AAVE() {
18481
- try {
18482
-
18483
- const [graphType, category, param1, param2] = argsToArray(arguments);
18484
-
18485
-
18486
- validateParams(aaveParamsSchema, { graphType, category, param1, param2 });
18487
-
18488
- const baseUrl = 'https://onchain-proxy.fileverse.io/third-party';
18489
- const url =
18490
- `${baseUrl}` +
18491
- `?service=aave` +
18492
- `&graphType=${encodeURIComponent(graphType)}` +
18493
- `&category=${encodeURIComponent(category)}` +
18494
- `&input1=${encodeURIComponent(param1)}` +
18495
- (param2 ? `&input2=${encodeURIComponent(param2)}` : '');
18496
-
18497
- const res = await fetch(url);
18498
- if (!res.ok) {
18499
- throw new NetworkError('AAVE', res.status)
18500
- }
18501
-
18502
- const json = await res.json();
18503
- if (Array.isArray(json)) {
18504
- return json.map(item => {
18505
- const flat = {};
18506
- Object.entries(item).forEach(([k, v]) => {
18507
- if (v === null || typeof v !== 'object') flat[k] = v;
18508
- });
18509
- return flat
18510
- })
18511
- }
18512
- return json
18513
- } catch (err) {
18514
- return errorMessageHandler(err, 'AAVE')
18515
- }
18459
+ async function FLVURL() {
18460
+ return new Promise((resolve) => {
18461
+ setTimeout(() => {
18462
+ resolve([{ Yoo: 'gotcha' }]);
18463
+ }, 10000);
18464
+ })
18516
18465
  }
18517
18466
 
18518
18467
  function POLYMARKET() {