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