@fileverse-dev/formulajs 4.4.11-mod-83 → 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,375 +13186,86 @@ class InvalidApiKeyError extends Error {
13187
13186
  }
13188
13187
  }
13189
13188
 
13190
- let PROXY_MAP;
13191
-
13192
- function initializeProxyMap() {
13193
- if (!PROXY_MAP) {
13194
- const fileverseProxyUrl = `${window.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`;
13195
-
13196
- PROXY_MAP = {
13197
- Etherscan: {
13198
- url: fileverseProxyUrl,
13199
- removeParams: ['apikey']
13200
- },
13201
- Basescan: {
13202
- url: fileverseProxyUrl,
13203
- removeParams: ['apikey']
13204
- },
13205
- Gnosisscan: {
13206
- url: fileverseProxyUrl,
13207
- removeParams: ['apikey']
13208
- },
13209
- Coingecko: {
13210
- url: fileverseProxyUrl,
13211
- removeParams: ['apikey']
13212
- },
13213
- Firefly: {
13214
- url: fileverseProxyUrl,
13215
- removeParams: ['apikey']
13216
- },
13217
- Neynar: {
13218
- url: fileverseProxyUrl,
13219
- removeParams: ['api_key']
13220
- },
13221
- Safe: {
13222
- url: fileverseProxyUrl,
13223
- removeParams: ['api_key']
13224
- },
13225
- Defillama: {
13226
- url: fileverseProxyUrl,
13227
- removeParams: ['api_key']
13228
- },
13229
- GnosisPay: {
13230
- url: fileverseProxyUrl,
13231
- removeParams: ['api_key']
13232
- },
13233
- // Add more services as needed. It can be direct url instead of ENV variable
13234
- // ANOTHER_SERVICE: "https://another-proxy-url.com"
13235
- };
13236
- }
13237
- return PROXY_MAP;
13238
- }
13239
-
13240
- /**
13241
- * Removes specified parameters from a URL
13242
- * @param {string} url - The original URL
13243
- * @param {string[]} paramsToRemove - Array of parameter names to remove
13244
- * @returns {string} URL with specified parameters removed
13245
- */
13246
- function removeUrlParams(url, paramsToRemove) {
13247
- if (!paramsToRemove || paramsToRemove.length === 0) {
13248
- 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
+ }
13249
13198
  }
13250
13199
 
13251
- const urlObj = new URL(url);
13252
-
13253
- paramsToRemove.forEach(param => {
13254
- if (urlObj.searchParams.has(param)) {
13255
- urlObj.searchParams.delete(param);
13256
- }
13257
- });
13258
-
13259
- return urlObj.toString();
13260
- }
13261
-
13262
- /**
13263
- * Handles URL routing through proxy or direct API calls
13264
- * @param {string} url - The original API URL
13265
- * @param {string} serviceName - [OPTIONAL] The name of the service (e.g., 'EOA')
13266
- * @param {object} headers - [OPTIONAL] The name of the service (e.g., 'EOA')
13267
- * @returns {Object} Object containing URL and HEADERS for the fetch request
13268
- */
13269
- function getUrlAndHeaders({ url, serviceName, headers = {} }) {
13270
- const proxyMap = initializeProxyMap();
13271
- // Check if proxy is enabled in localStorage
13272
- const apiKeyLS = window.localStorage.getItem(SERVICES_API_KEY[serviceName]);
13273
- const isProxyModeEnabledValue = apiKeyLS === 'DEFAULT_PROXY_MODE';
13274
-
13275
- // Check if proxy URL exists for this service
13276
- const proxyConfig = proxyMap[serviceName];
13277
-
13278
- if (!proxyConfig && SERVICES_API_KEY[serviceName] && (!apiKeyLS || apiKeyLS === '')) {
13279
- throw new MissingApiKeyError(SERVICES_API_KEY[serviceName])
13280
- }
13200
+ case err instanceof MissingApiKeyError :
13201
+ return {
13202
+ message: err.message,
13203
+ functionName,
13204
+ type: ERROR_MESSAGES_FLAG.MISSING_KEY
13205
+ }
13281
13206
 
13282
- // If proxy mode is enabled AND proxy URL exists for this service
13283
- if ((isProxyModeEnabledValue || !apiKeyLS || apiKeyLS === '') && proxyConfig) {
13284
- // Remove specified parameters from the target URL
13285
- 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
+ }
13286
13214
 
13215
+ case err instanceof NetworkError:
13216
+ if(err.status === 429){
13287
13217
  return {
13288
- URL: proxyConfig.url,
13289
- HEADERS: {
13290
- 'target-url': cleanedUrl,
13291
- method: 'GET',
13292
- 'Content-Type': 'application/json'
13293
- }
13294
- };
13295
- }
13296
-
13297
- return {
13298
- URL: url,
13299
- HEADERS: {
13300
- ...headers,
13301
- }
13302
- };
13303
- }
13304
-
13305
- const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
13306
- if (!timestamp || !chain) return
13307
- const chainId = CHAIN_ID_MAP[chain];
13308
- const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
13309
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
13310
- const res = await fetch(finalUrl, {
13311
- method: 'GET',
13312
- headers: HEADERS,
13313
- });
13314
- const json = await res.json();
13315
- return parseInt(json.result);
13316
-
13317
- };
13318
-
13319
- var fromTimestampToBlock = {
13320
- fromTimeStampToBlock
13321
- };
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
+ }
13322
13231
 
13323
- function toTimestamp(dateStr) {
13324
- // Expecting format: "DD/MM/YYYY"
13325
- const [day, month, year] = dateStr.split("/").map(Number);
13326
- const date = new Date(year, month - 1, day);
13327
- return Math.floor(date.getTime() / 1000); // Unix timestamp in seconds
13328
- }
13232
+ case err instanceof EnsError:
13233
+ return {
13234
+ message: err.message,
13235
+ functionName,
13236
+ type: ERROR_MESSAGES_FLAG.ENS
13237
+ }
13329
13238
 
13330
- const isAddress = (input) => {
13331
- return (/^0x[a-fA-F0-9]{40}$/.test(input))
13332
- };
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
+ }
13333
13246
 
13334
- var isAddress$1 = {
13335
- 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
+ }
13336
13255
  };
13337
13256
 
13338
- /* global document */
13339
- /* global window */
13340
- /* global ethers */
13341
-
13342
13257
 
13343
13258
 
13344
- async function fromEnsNameToAddress(name) {
13345
- if (typeof ethers === 'undefined') {
13346
- await new Promise((resolve, reject) => {
13347
- const script = document.createElement('script');
13348
- script.src = 'https://cdn.jsdelivr.net/npm/ethers@6.10.0/dist/ethers.umd.min.js';
13349
- script.onload = resolve;
13350
- script.onerror = reject;
13351
- document.head.appendChild(script);
13352
- });
13353
- }
13354
-
13355
- const ALCHEMY_KEY = window.localStorage.getItem(UTILITY.ALCHEMY_API_KEY);
13356
- if (!ALCHEMY_KEY) {
13357
- console.error('alchemy api key missing');
13358
- return null
13359
- }
13360
- const provider = new ethers.AlchemyProvider('mainnet', ALCHEMY_KEY);
13361
13259
 
13362
- try {
13363
- const resolved = await provider.resolveName(name); // ENS ➝ address
13364
- return resolved || null
13365
- } catch (err) {
13366
- console.error('ENS resolution failed:', err.message);
13367
- 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
+ )
13368
13267
  }
13369
- }
13370
- const validateAndGetAddress = async (address) => {
13371
- if(isAddress$1.isAddress(address)) return address
13372
-
13373
- const resolvedAddress = await fromEnsNameToAddress(address);
13374
- if(resolvedAddress) return resolvedAddress
13375
- throw new ValidationError("Invalid address")
13376
- };
13377
-
13378
- var fromEnsNameToAddress$1 = {
13379
- validateAndGetAddress
13380
- };
13381
-
13382
- async function handleScanRequest({
13383
- type,
13384
- address,
13385
- startDate,
13386
- endDate,
13387
- page = 1,
13388
- offset = 10,
13389
- apiKey,
13390
- functionName,
13391
- chainId,
13392
- network
13393
- }) {
13394
- const API_INFO_MAP = {
13395
- BASE: { url: 'https://api.basescan.org/api', apiKeyName: SERVICES_API_KEY.Basescan },
13396
- ETHERSCAN: { url: 'https://api.etherscan.io/v2/api', apiKeyName: SERVICES_API_KEY.Etherscan },
13397
- GNOSIS: { url: 'https://api.gnosisscan.io/api', apiKeyName: SERVICES_API_KEY.Gnosisscan }
13398
- };
13399
-
13400
- if (type !== 'gas') {
13401
- address = await fromEnsNameToAddress$1.validateAndGetAddress(address);
13402
- }
13403
-
13404
- const apiInfo = API_INFO_MAP[functionName];
13405
- const baseUrl = apiInfo?.url;
13406
- if (!baseUrl) throw new ValidationError(`Api not found for: ${functionName}`)
13407
-
13408
-
13409
- const ACTION_MAP = {
13410
- 'all-txns': 'txlist',
13411
- 'token-txns': 'tokentx',
13412
- 'nft-txns': 'tokennfttx',
13413
- 'gas': 'gasoracle'
13414
- };
13415
-
13416
- const action = ACTION_MAP[type];
13417
- if (!action) throw new ValidationError(`Invalid type: ${type}`)
13418
-
13419
- const module = action === 'gasoracle' ? 'gastracker' : 'account';
13420
- let url = `${baseUrl}?chainid=${chainId}&module=${module}&action=${action}&apikey=${apiKey}`;
13421
-
13422
- if (['all-txns', 'token-txns', 'nft-txns'].includes(type)) {
13423
- url += `&address=${address}&startblock=0&endblock=99999999&sort=asc`;
13424
-
13425
- if (!isNaN(startDate) && !isNaN(endDate)) {
13426
- const [startBlock, endBlock] = await Promise.all([
13427
- fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startDate), network, apiKey),
13428
- fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endDate), network, apiKey)
13429
- ]);
13430
- url += `&startblock=${startBlock || '0'}&endblock=${endBlock || '99999999'}`;
13431
- }
13432
- url += `&page=${page}&offset=${offset}`;
13433
- }
13434
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({url, serviceName: apiInfo.apiKeyName, headers: {}});
13435
- const res = await fetch(finalUrl, {
13436
- method: 'GET',
13437
- headers: HEADERS,
13438
- });
13439
- if (!res.ok) {
13440
- throw new NetworkError(apiInfo.apiKeyName, res.status)
13441
- }
13442
- const json = await res.json();
13443
-
13444
- if (typeof json.result === 'string') {
13445
- if (json.result.includes('Invalid API Key')){
13446
- throw new InvalidApiKeyError(apiInfo.apiKeyName)
13447
- }
13448
-
13449
- if (json.result.includes('Max rate limit reached'))
13450
- throw new RateLimitError(apiInfo.apiKeyName)
13451
- }
13452
-
13453
- return type === 'gas' && !Array.isArray(json.result) ? [json.result] : json.result
13454
- }
13455
-
13456
- const fromUsernameToFid = async (username, apiKey) => {
13457
- if (!username) return null
13458
- const url = `https://api.neynar.com/v2/farcaster/user/search/?q=${username}&limit=5`;
13459
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
13460
- url, serviceName: 'Neynar', headers: {
13461
- 'x-api-key': apiKey,
13462
- 'x-neynar-experimental': 'false'
13463
- }
13464
- });
13465
-
13466
- const res = await fetch(finalUrl, {
13467
- method: 'GET',
13468
- headers: HEADERS,
13469
- });
13470
- const json = await res.json();
13471
- const users = json.result ? json.result.users : [];
13472
- const user = users.find(user => user.username === username);
13473
- return user && user.fid || null;
13474
- };
13475
- var fromUsernameToFid$1 = {
13476
- fromUsernameToFid
13477
- };
13478
-
13479
- const errorMessageHandler = (err, functionName) => {
13480
-
13481
- switch (true) {
13482
- case err instanceof ValidationError : {
13483
- return {
13484
- message: err.message,
13485
- functionName,
13486
- type: ERROR_MESSAGES_FLAG.INVALID_PARAM
13487
- }
13488
- }
13489
-
13490
- case err instanceof MissingApiKeyError :
13491
- return {
13492
- message: err.message,
13493
- functionName,
13494
- type: ERROR_MESSAGES_FLAG.MISSING_KEY
13495
- }
13496
-
13497
- case err instanceof RateLimitError:
13498
- return {
13499
- message: err.message,
13500
- functionName,
13501
- type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13502
- apiKeyName: err.api
13503
- }
13504
-
13505
- case err instanceof NetworkError:
13506
- if(err.status === 429){
13507
- return {
13508
- message: err.message,
13509
- functionName,
13510
- type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
13511
- apiKeyName: err.api
13512
- }
13513
- } else {
13514
- return {
13515
- message: err.message,
13516
- functionName,
13517
- type: ERROR_MESSAGES_FLAG.NETWORK_ERROR,
13518
- apiKeyName: err.api
13519
- }
13520
- }
13521
-
13522
- case err instanceof EnsError:
13523
- return {
13524
- message: err.message,
13525
- functionName,
13526
- type: ERROR_MESSAGES_FLAG.ENS
13527
- }
13528
-
13529
- case err instanceof InvalidApiKeyError:
13530
- return {
13531
- message: err.message,
13532
- functionName,
13533
- type: ERROR_MESSAGES_FLAG.INVALID_API_KEY,
13534
- apiKeyName: err.api
13535
- }
13536
-
13537
- default:
13538
- return {
13539
- message: 'An unexpected error occured',
13540
- functionName,
13541
- type: ERROR_MESSAGES_FLAG.DEFAULT,
13542
- reason: err.message || err
13543
- }
13544
- }
13545
- };
13546
-
13547
-
13548
-
13549
-
13550
- function validateParams(schema, rawParams) {
13551
- const result = schema.safeParse(rawParams);
13552
- if (!result.success) {
13553
- const issue = result.error.issues[0];
13554
- throw new ValidationError(
13555
- issue.message
13556
- )
13557
- }
13558
- return result.data
13268
+ return result.data
13559
13269
  }
13560
13270
 
13561
13271
  var util;
@@ -17474,51 +17184,50 @@ ZodOptional.create;
17474
17184
  ZodNullable.create;
17475
17185
  const preprocessType = ZodEffects.createWithPreprocess;
17476
17186
 
17477
- const farcasterSchema = objectType({
17478
- platform: literalType('farcaster'),
17479
- contentType: enumType(['posts', 'replies', 'channels']),
17480
- identifier: stringType().nonempty(),
17481
- start: numberType().int().nonnegative().default(0),
17482
- 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(),
17483
17192
  });
17484
17193
 
17485
- const lensSchema = objectType({
17486
- platform: literalType('lens'),
17487
- contentType: enumType(['posts', 'replies']),
17488
- identifier: stringType().nonempty(),
17489
- start: numberType().int().nonnegative().default(0),
17490
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17491
- });
17194
+ async function AAVE() {
17195
+ try {
17492
17196
 
17493
- const fireflyParamsSchema = discriminatedUnionType('platform', [
17494
- farcasterSchema,
17495
- lensSchema,
17496
- ]);
17497
- const fireFlyPlaformType = {
17498
- farcaster: {
17499
- posts: 'farcasterid',
17500
- replies: 'farcasterpostid',
17501
- channels: 'farcasterchannels'
17502
- },
17503
- lens: {
17504
- posts: 'lensid',
17505
- replies: 'lenspostid'
17506
- }
17507
- };
17197
+ const [graphType, category, param1, param2] = argsToArray(arguments);
17508
17198
 
17509
- const lensParamsSchema = objectType({
17510
- contentType: enumType(['posts', 'replies']),
17511
- identifier: stringType().nonempty(),
17512
- start: numberType().int().nonnegative().default(0),
17513
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17514
- });
17515
17199
 
17516
- const farcasterParamsSchema = objectType({
17517
- contentType: enumType(['posts', 'replies', 'channels']),
17518
- identifier: stringType().nonempty(),
17519
- start: numberType().int().nonnegative().default(0),
17520
- end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17521
- });
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
+ }
17522
17231
 
17523
17232
  const dateStringToTimestamp = (val) => {
17524
17233
  const [dd, mm, yyyy] = val.split('/');
@@ -17539,6 +17248,440 @@ const dateOrTimestamp = preprocessType(
17539
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' })
17540
17249
  );
17541
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
+
17542
17685
  const blockscoutParamsSchema = objectType({
17543
17686
  address: stringType().nonempty(),
17544
17687
  type: enumType(['stat', 'txns', 'tokens']),
@@ -17549,44 +17692,78 @@ const blockscoutParamsSchema = objectType({
17549
17692
  offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17550
17693
  });
17551
17694
 
17552
- const gasSchema$1 = objectType({
17553
- type: literalType('gas'),
17554
- startDate: dateOrTimestamp.optional(),
17555
- endDate: dateOrTimestamp.optional(),
17556
- page: numberType().int().nonnegative().default(1),
17557
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17558
- });
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);
17559
17706
 
17560
- const txnSchema$1 = objectType({
17561
- type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17562
- address: stringType().nonempty(),
17563
- startDate: dateOrTimestamp.optional(),
17564
- endDate: dateOrTimestamp.optional(),
17565
- page: numberType().int().nonnegative().default(1),
17566
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17567
- });
17707
+ validateParams(blockscoutParamsSchema, {
17708
+ address,
17709
+ type,
17710
+ chain,
17711
+ startTimestamp,
17712
+ endTimestamp,
17713
+ page,
17714
+ offset
17715
+ });
17568
17716
 
17569
- 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;
17570
17720
 
17571
- const gasSchema = objectType({
17572
- type: literalType('gas'),
17573
- startDate: dateOrTimestamp.optional(),
17574
- endDate: dateOrTimestamp.optional(),
17575
- page: numberType().int().nonnegative().default(1),
17576
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17577
- });
17721
+ const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
17578
17722
 
17579
- const txnSchema = objectType({
17580
- type: enumType(['all-txns', 'token-txns', 'nft-txns']),
17581
- address: stringType().nonempty(),
17582
- startDate: dateOrTimestamp.optional(),
17583
- endDate: dateOrTimestamp.optional(),
17584
- chain: enumType(['ethereum','base','gnosis']),
17585
- page: numberType().int().nonnegative().default(1),
17586
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17587
- });
17723
+ const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
17588
17724
 
17589
- 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
+ }
17590
17767
 
17591
17768
  const allowedValues = ['1h', '24h', '7d'];
17592
17769
  const param2Schema = stringType()
@@ -17627,123 +17804,67 @@ const coingeckoParamsSchema = discriminatedUnionType('category', [
17627
17804
  derivativesSchema,
17628
17805
  ]);
17629
17806
 
17630
- const baseSchema = objectType({
17631
- addresses: stringType().nonempty(),
17632
- category: enumType(['balance','txns']),
17633
- chains: preprocessType(
17634
- (val) => typeof val === 'string'
17635
- ? val.split(',').map(s => s.trim()).filter(Boolean)
17636
- : val,
17637
- arrayType(
17638
- enumType(['ethereum','gnosis','base'])
17639
- ).nonempty()
17640
- ),
17641
- startTime: dateOrTimestamp.optional(),
17642
- endTime: dateOrTimestamp.optional(),
17643
- page: numberType().int().nonnegative().default(1),
17644
- offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17645
- });
17646
-
17647
- const eoaParamsSchema = preprocessType(
17648
- (val) => {
17649
- const obj = { ...(val || {}) };
17650
- // if balance, ignore startTime/endTime
17651
- if (obj.category === 'balance') {
17652
- delete obj.startTime;
17653
- delete obj.endTime;
17654
- }
17655
- return obj
17656
- },
17657
- baseSchema.refine(data => {
17658
- // for txns, startTime and endTime are required
17659
- if (data.category === 'txns') {
17660
- return data.startTime !== undefined && data.endTime !== undefined
17661
- }
17662
- return true
17663
- }, {
17664
- message: 'startTime and endTime required for txns',
17665
- path: ['startTime'],
17666
- })
17667
- );
17668
-
17669
- const safeParamsSchema = objectType({
17670
- address: stringType().nonempty(),
17671
- utility: literalType('txns'),
17672
- chain: enumType(['ethereum','gnosis']),
17673
- limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
17674
- offset: numberType().int().nonnegative().default(0),
17675
- });
17676
-
17677
- const categories = ['protocols','yields','dex','fees'];
17678
- const defillamaParamsSchema = objectType({
17679
- category: enumType(categories)
17680
- });
17681
-
17682
- const CATEGORY_URLS = {
17683
- protocols: 'https://api.llama.fi/protocols',
17684
- yields: 'https://yields.llama.fi/pools',
17685
- dex: 'https://api.llama.fi/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true',
17686
- fees: 'https://api.llama.fi/overview/fees?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true&dataType=dailyFees'
17687
- };
17688
-
17689
- const uniswapParamsSchema = objectType({
17690
- graphType: enumType(['v3','v3-raw']),
17691
- category: enumType(['tokens','markets']),
17692
- param1: stringType().nonempty(),
17693
- param2: stringType().optional(),
17694
- });
17695
-
17696
- const aaveParamsSchema = objectType({
17697
- graphType: enumType(['v2','v2-raw']),
17698
- category: enumType(['tokens','markets']),
17699
- param1: stringType().nonempty(),
17700
- param2: stringType().optional(),
17701
- });
17702
-
17703
17807
  /* global window */
17704
17808
 
17705
17809
 
17706
17810
 
17707
- async function FIREFLY() {
17811
+ async function COINGECKO() {
17708
17812
  try {
17709
- const [platform, contentType, identifier, start = 0, end = 10] = argsToArray(arguments);
17710
-
17711
- validateParams(fireflyParamsSchema, {
17712
- platform,
17713
- contentType,
17714
- identifier,
17715
- start,
17716
- end,
17717
- });
17813
+ const [category, param1, param2] = argsToArray(arguments);
17814
+ validateParams(coingeckoParamsSchema, { category, param1, param2 });
17718
17815
 
17719
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Firefly);
17816
+ const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
17720
17817
 
17721
- const url = new URL('https://openapi.firefly.land/v1/fileverse/fetch');
17722
- url.searchParams
17723
- .set('query',
17724
- identifier
17725
- .split(',')
17726
- .map(s => s.trim())
17727
- .filter(Boolean)
17728
- .join(',')
17729
- );
17730
- url.searchParams.set('type', fireFlyPlaformType[platform][contentType]);
17731
- url.searchParams.set('start', String(start));
17732
- 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});
17733
17850
 
17734
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17735
- const response = await fetch(finalUrl, {
17736
- method: 'GET',
17737
- headers: HEADERS,
17738
- });
17739
- if (!response.ok) {
17740
- 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)
17741
17857
  }
17742
17858
 
17743
- const { data } = await response.json();
17744
- if (!Array.isArray(data)) {
17745
- 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]
17746
17865
  }
17866
+
17867
+ const data = Array.isArray(json) ? json : [json];
17747
17868
  return data.map(item => {
17748
17869
  const flat = {};
17749
17870
  for (const [key, value] of Object.entries(item)) {
@@ -17751,78 +17872,133 @@ async function FIREFLY() {
17751
17872
  flat[key] = value;
17752
17873
  }
17753
17874
  }
17754
- flat.platform = platform;
17755
17875
  return flat
17756
17876
  })
17757
-
17758
17877
  } catch (err) {
17759
- return errorMessageHandler(err, 'FIREFLY')
17878
+ return errorMessageHandler(err, 'COINGECKO')
17760
17879
  }
17761
17880
  }
17762
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
+ };
17763
17893
 
17764
- async function LENS() {
17894
+ async function DEFILLAMA() {
17765
17895
  try {
17766
- const [contentType, identifier, start = 0, end = 10] =
17767
- 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();
17768
17903
 
17769
- validateParams(lensParamsSchema, {
17770
- contentType,
17771
- identifier,
17772
- start,
17773
- end,
17774
- });
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
+ }
17775
17916
 
17776
- const apiKey = window.localStorage.getItem(
17777
- SERVICES_API_KEY.Firefly
17778
- );
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
+ }
17779
17928
 
17780
- const url = new URL(
17781
- 'https://openapi.firefly.land/v1/fileverse/fetch'
17782
- );
17783
- url.searchParams.set(
17784
- 'query',
17785
- identifier
17786
- .split(',')
17787
- .map((s) => s.trim())
17788
- .filter(Boolean)
17789
- .join(',')
17790
- );
17791
- const typeMap = {
17792
- posts: 'lensid',
17793
- replies: 'lenspostid',
17794
- };
17795
- url.searchParams.set('type', typeMap[contentType]);
17796
- url.searchParams.set('start', String(start));
17797
- 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
+ });
17798
17936
 
17799
- 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
+ });
17800
17946
 
17801
- const response = await fetch(finalUrl, {
17802
- method: 'GET',
17803
- headers: HEADERS,
17804
- });
17805
- if (!response.ok) {
17806
- throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
17807
- }
17947
+ const etherscanParamsSchema = discriminatedUnionType('type', [gasSchema, txnSchema]);
17808
17948
 
17809
- const { data } = await response.json();
17810
- if (!Array.isArray(data)) return []
17949
+ /* global window */
17811
17950
 
17812
- return data.map((item) => {
17813
- const flat = {};
17814
- for (const [key, value] of Object.entries(item)) {
17815
- if (value == null || typeof value !== 'object') {
17816
- flat[key] = value;
17817
- }
17818
- }
17819
- flat.platform = 'lens';
17820
- 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,
17821
17980
  })
17822
17981
  } catch (err) {
17823
- return errorMessageHandler(err, 'LENS')
17982
+ return errorMessageHandler(err, 'ETHERSCAN')
17824
17983
  }
17825
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
+
17826
18002
  async function FARCASTER() {
17827
18003
  try {
17828
18004
  const [contentType, identifier, start = 0, end = 10] =
@@ -17889,102 +18065,108 @@ async function FARCASTER() {
17889
18065
  }
17890
18066
  }
17891
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
+ });
17892
18083
 
17893
- async function BLOCKSCOUT() {
17894
- try {
17895
- const [
17896
- address,
17897
- type,
17898
- chain = 'ethereum',
17899
- startTimestamp,
17900
- endTimestamp,
17901
- page,
17902
- offset,
17903
- ] = 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
+ };
17904
18099
 
17905
- validateParams(blockscoutParamsSchema, {
17906
- address,
17907
- type,
17908
- chain,
17909
- startTimestamp,
17910
- endTimestamp,
17911
- page,
17912
- offset
17913
- });
18100
+ /* global window */
17914
18101
 
17915
- const startTs =
17916
- startTimestamp ?? Math.floor((Date.now() - 30 * 24 * 3600 * 1000) / 1000);
17917
- const endTs = endTimestamp;
17918
18102
 
17919
- const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
17920
18103
 
17921
- const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
17922
18104
 
17923
- let requestUrl;
17924
- switch (type) {
17925
- case 'stat':
17926
- requestUrl = `${hostname}/api/v2/addresses/${resolvedAddress}/counters`;
17927
- break
17928
- case 'txns':
17929
- requestUrl =
17930
- `${hostname}/api?module=account&action=txlist` +
17931
- `&address=${resolvedAddress}` +
17932
- `&start_timestamp=${startTs}` +
17933
- `&end_timestamp=${endTs ?? ''}` +
17934
- `&page=${page}` +
17935
- `&offset=${offset}` +
17936
- `&sort=asc`;
17937
- break
17938
- case 'tokens':
17939
- requestUrl =
17940
- `${hostname}/api?module=account&action=tokenlist` +
17941
- `&address=${resolvedAddress}`;
17942
- break
17943
- }
17944
18105
 
17945
- 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
+ });
17946
18139
  if (!response.ok) {
17947
- throw new NetworkError('BLOCKSCOUT', response.status)
18140
+ throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
17948
18141
  }
17949
18142
 
17950
- const json = await response.json();
17951
-
17952
- // custom error conditions
17953
- if (json?.result?.includes('Invalid parameter(s)')) {
17954
- throw new ValidationError('Invalid parameters')
17955
- }
17956
- if (json?.result?.includes('Not found')) {
17957
- throw new ValidationError('Address information not found')
18143
+ const { data } = await response.json();
18144
+ if (!Array.isArray(data)) {
18145
+ return []
17958
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
+ })
17959
18157
 
17960
- return type === 'stat' ? [json] : json.result
17961
18158
  } catch (err) {
17962
- return errorMessageHandler(err, 'BLOCKSCOUT')
18159
+ return errorMessageHandler(err, 'FIREFLY')
17963
18160
  }
17964
18161
  }
17965
18162
 
17966
- async function BASE() {
17967
- try {
17968
- const [type, address, startDate, endDate, page, limit] = argsToArray(arguments);
17969
- validateParams(baseParamsSchema, { type, address, startDate, endDate, page, limit });
17970
- const API_KEY = window.localStorage.getItem(SERVICES_API_KEY.Basescan);
18163
+ /* global window */
18164
+
18165
+
18166
+
18167
+
18168
+
17971
18169
 
17972
- return await handleScanRequest({
17973
- type,
17974
- address,
17975
- startDate,
17976
- endDate,
17977
- page,
17978
- offset: limit,
17979
- apiKey: API_KEY,
17980
- functionName: 'BASE',
17981
- chainId: CHAIN_ID_MAP.base,
17982
- network: 'base'
17983
- })
17984
- } catch (error) {
17985
- return errorMessageHandler(error, 'BASE')
17986
- }
17987
- }
17988
18170
  async function GNOSIS() {
17989
18171
  try {
17990
18172
  const [type, address, startDate, endDate, page = 1, limit = 10] =
@@ -18021,306 +18203,178 @@ async function GNOSIS() {
18021
18203
  }
18022
18204
  }
18023
18205
 
18024
- async function NEYNAR() {
18025
- try {
18026
- const neynarParamsSchema = objectType({
18027
- username: stringType().nonempty()
18028
- });
18029
-
18030
- const [username] = argsToArray(arguments);
18031
-
18032
- validateParams(neynarParamsSchema, { username });
18033
-
18034
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Neynar);
18035
-
18036
- const fid = await fromUsernameToFid$1.fromUsernameToFid(username, apiKey);
18037
- if (!fid) throw new ValidationError(`Invalid username: ${username}`)
18038
-
18039
- const url = `https://api.neynar.com/v2/farcaster/followers?fid=${fid}`;
18040
-
18041
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
18042
- url: url.toString(), serviceName: 'Neynar',
18043
- headers: {
18044
- 'x-api-key': apiKey,
18045
- 'x-neynar-experimental': 'false'
18046
- }
18047
-
18048
- });
18049
-
18050
- const response = await fetch(finalUrl, {
18051
- method: 'GET',
18052
- headers: HEADERS,
18053
- });
18054
- if (!response.ok) {
18055
- throw new NetworkError(SERVICES_API_KEY.Neynar, response.status)
18056
- }
18057
-
18058
- const json = await response.json();
18059
- const users = json?.users || [];
18060
- if (!users.length) return []
18061
-
18062
- return users.map(({ user }) => ({
18063
- username: user.username,
18064
- custody_address: user.custody_address,
18065
- follower_count: user.follower_count,
18066
- country: user.profile?.location?.address?.country || '',
18067
- city: user.profile?.location?.address?.city || '',
18068
- }))
18069
- } catch (err) {
18070
- return errorMessageHandler(err, 'NEYNAR')
18071
- }
18072
- }
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
+ });
18073
18212
 
18074
- // export async function GNOSISPAY({
18075
- // cardId,
18076
- // startDate,
18077
- // endDate,
18078
- // limit = 20,
18079
- // offset = 0,
18080
- // }) {
18081
- // const apiKeyKey = SERVICES_API_KEY.GnosisPay
18082
- // const API_KEY = window.localStorage.getItem(apiKeyKey);
18083
- // if (!API_KEY) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.MISSING_KEY}`;
18084
- // if (!cardId) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.INVALID_PARAM}`;
18085
- // if(limit > MAX_PAGE_LIMIT){
18086
- // return ERROR_MESSAGES_FLAG.MAX_PAGE_LIMIT
18087
- // }
18213
+ /* global window */
18088
18214
 
18089
- // const url = new URL(`https://api.gnosispay.com/cards/${cardId}/transactions`);
18090
- // url.searchParams.set('limit', limit.toString());
18091
- // url.searchParams.set('offset', offset.toString());
18092
18215
 
18093
- // if (!isNaN(toTimestamp(startDate))) {
18094
- // url.searchParams.set('startDate', new Date(startDate * 1000).toISOString());
18095
- // }
18096
18216
 
18097
- // if (!isNaN(toTimestamp(endDate))) {
18098
- // url.searchParams.set('endDate', new Date(endDate * 1000).toISOString());
18099
- // }
18100
18217
 
18101
- // try {
18102
- // const res = await fetch(url.toString(), {
18103
- // headers: {
18104
- // Authorization: `Bearer ${API_KEY}`,
18105
- // 'Content-Type': 'application/json',
18106
- // },
18107
- // });
18108
-
18109
- // if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
18110
-
18111
- // const json = await res.json();
18112
-
18113
- // if (!Array.isArray(json)) return [];
18114
-
18115
- // return json.map(tx => ({
18116
- // createdAt: tx.createdAt,
18117
- // clearedAt: tx.clearedAt,
18118
- // country: tx.country,
18119
- // merchant: tx.merchant,
18120
- // billingAmount: tx.billingAmount,
18121
- // billingCurrency: tx.billingCurrency,
18122
- // transactionAmount: tx.transactionAmount,
18123
- // transactionCurrency: tx.transactionCurrency,
18124
- // transactionType: tx.transactionType,
18125
- // kind: tx.kind,
18126
- // status: tx.status || null,
18127
- // mcc: tx.mcc,
18128
- // }));
18129
- // } catch (err) {
18130
- // console.error('GNOSISPAY_CARD_TXNS error:', err);
18131
- // return ERROR_MESSAGES_FLAG.DEFAULT;
18132
- // }
18133
- // }
18134
18218
 
18135
- async function ETHERSCAN() {
18219
+ async function LENS() {
18136
18220
  try {
18137
- const [type, chain, address, startDate, endDate, page = 1, limit = 10] =
18221
+ const [contentType, identifier, start = 0, end = 10] =
18138
18222
  argsToArray(arguments);
18139
18223
 
18224
+ validateParams(lensParamsSchema, {
18225
+ contentType,
18226
+ identifier,
18227
+ start,
18228
+ end,
18229
+ });
18140
18230
 
18141
- validateParams(etherscanParamsSchema, { type, chain, address, startDate, endDate, page, limit });
18142
-
18143
- const chainId = CHAIN_ID_MAP[chain];
18144
- if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
18145
-
18146
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
18147
-
18148
- return await handleScanRequest({
18149
- type,
18150
- address,
18151
- startDate,
18152
- endDate,
18153
- page,
18154
- offset: limit,
18155
- apiKey,
18156
- functionName: 'ETHERSCAN',
18157
- chainId,
18158
- network: chain,
18159
- })
18160
- } catch (err) {
18161
- return errorMessageHandler(err, 'ETHERSCAN')
18162
- }
18163
- }
18164
-
18165
-
18166
- async function COINGECKO() {
18167
- try {
18168
- const [category, param1, param2] = argsToArray(arguments);
18169
- validateParams(coingeckoParamsSchema, { category, param1, param2 });
18170
-
18171
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
18231
+ const apiKey = window.localStorage.getItem(
18232
+ SERVICES_API_KEY.Firefly
18233
+ );
18172
18234
 
18173
- const headers = {
18174
- accept: 'application/json',
18175
- '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',
18176
18249
  };
18177
- let url = '';
18178
- switch (category?.toLowerCase?.()) {
18179
- case 'price': {
18180
- const vs = param2 || 'usd';
18181
- url = `https://api.coingecko.com/api/v3/simple/price?vs_currencies=${vs}&symbols=${param1}`;
18182
- break
18183
- }
18184
- case 'market': {
18185
- 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' };
18186
- const _category = map[param1] || '';
18187
- const trend = param2 ? `&price_change_percentage=${param2}` : '';
18188
- url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category ? `&category=${_category}` : ''}${trend}`;
18189
- break
18190
- }
18191
- case 'stablecoins': {
18192
- const _category = param1 === 'all' ? 'stablecoins' : param1;
18193
- const trend = param2 ? `&price_change_percentage=${param2}` : '';
18194
- url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=${_category}&order=market_cap_desc&page=1&per_page=100${trend}`;
18195
- break
18196
- }
18197
- case 'derivatives': {
18198
- url = (!param1 || param1 === 'all')
18199
- ? 'https://api.coingecko.com/api/v3/derivatives'
18200
- : `https://api.coingecko.com/api/v3/derivatives/exchanges/${param1}?include_tickers=all`;
18201
- break
18202
- }
18203
- }
18204
- 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));
18205
18253
 
18206
- const res = await fetch(finalUrl, { headers: HEADERS });
18207
- const json = await res.json();
18208
- if (!res.ok) {
18209
- const msg = json?.status?.error_message || '';
18210
- if (msg.includes('API Key Missing')) throw new InvalidApiKeyError(SERVICES_API_KEY.Coingecko)
18211
- 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)
18212
18262
  }
18213
18263
 
18214
- if (category === 'price') {
18215
- const out = {};
18216
- for (const [token, prices] of Object.entries(json))
18217
- for (const [cur, val] of Object.entries(prices))
18218
- out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
18219
- return [out]
18220
- }
18264
+ const { data } = await response.json();
18265
+ if (!Array.isArray(data)) return []
18221
18266
 
18222
- const data = Array.isArray(json) ? json : [json];
18223
- return data.map(item => {
18267
+ return data.map((item) => {
18224
18268
  const flat = {};
18225
18269
  for (const [key, value] of Object.entries(item)) {
18226
- if (typeof value !== 'object' || value === null) {
18270
+ if (value == null || typeof value !== 'object') {
18227
18271
  flat[key] = value;
18228
18272
  }
18229
18273
  }
18274
+ flat.platform = 'lens';
18230
18275
  return flat
18231
18276
  })
18232
18277
  } catch (err) {
18233
- return errorMessageHandler(err, 'COINGECKO')
18278
+ return errorMessageHandler(err, 'LENS')
18234
18279
  }
18235
18280
  }
18236
18281
 
18237
- async function EOA() {
18238
- try {
18239
- const [addresses, category, chains, startTime, endTime, page = 1, offset = 10] =
18240
- argsToArray(arguments);
18241
- validateParams(eoaParamsSchema, { addresses, category, chains, startTime, endTime, page, offset });
18282
+ const neynarParamsSchema = objectType({
18283
+ username: stringType().nonempty()
18284
+ });
18242
18285
 
18243
- 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
+ });
18244
18295
 
18245
- const INPUTS = addresses.split(',').map(s => s.trim()).filter(Boolean);
18246
- 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
+ };
18247
18308
 
18248
- const ADDRESS_MAP = {};
18249
- for (const inp of INPUTS) {
18250
- if (isAddress$1.isAddress(inp)) {
18251
- ADDRESS_MAP[inp.toLowerCase()] = null;
18252
- } else {
18253
- const _address = await fromEnsNameToAddress$1.validateAndGetAddress(inp);
18254
- ADDRESS_MAP[_address.toLowerCase()] = _address;
18255
- }
18256
- }
18257
- const ADDRS = Object.keys(ADDRESS_MAP);
18258
- const out = [];
18309
+ /* global window */
18259
18310
 
18260
- async function fetchJSON(url) {
18261
- const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
18262
- const res = await fetch(finalUrl, {
18263
- method: 'GET',
18264
- headers: HEADERS,
18265
- });
18266
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Etherscan, res.status)
18267
- const json = await res.json();
18268
18311
 
18269
- if (typeof json.result === 'string') {
18270
- if (json.result.includes('Invalid API Key')) throw new InvalidApiKeyError(SERVICES_API_KEY.Etherscan)
18271
- if (json.result.includes('Max rate limit reached')) throw new RateLimitError(SERVICES_API_KEY.Etherscan)
18272
- }
18273
- return json.result
18274
- }
18275
18312
 
18276
18313
 
18277
- for (const chain of CHAINS) {
18278
- const chainId = CHAIN_ID_MAP[chain];
18279
- if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
18280
18314
 
18281
- if (category === 'balance') {
18282
- // chunk 20
18283
- for (let i = 0; i < ADDRS.length; i += 20) {
18284
- const slice = ADDRS.slice(i, i + 20).join(',');
18285
- const url =
18286
- `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18287
- `&module=account&action=addresstokenbalance&address=${slice}` +
18288
- `&page=${page}&offset=${offset}&apikey=${apiKey}`;
18289
- const data = await fetchJSON(url);
18290
- if (!Array.isArray(data)) return data
18291
- data.forEach((item, idx) => out.push({ chain, address: ADDRS[i + idx], name: ADDRESS_MAP[ADDRS[i + idx]], ...item }));
18292
- }
18293
- } else {
18294
- // txns
18295
- const sb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startTime), chain, apiKey);
18296
- const eb = await fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endTime), chain, apiKey);
18297
- if (!sb) throw new ValidationError(`Invalid startTime: ${startTime}`)
18298
- if (!eb) throw new ValidationError(`Invalid endTime: ${endTime}`)
18299
- for (const addr of ADDRS) {
18300
- const url =
18301
- `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18302
- `&module=account&action=tokentx&address=${addr}` +
18303
- `&startblock=${sb}&endblock=${eb}` +
18304
- `&page=${page}&offset=${offset}&sort=asc&apikey=${apiKey}`;
18305
- const data = await fetchJSON(url);
18306
- if (!Array.isArray(data)) return data
18307
- 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'
18308
18337
  }
18309
- }
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)
18310
18347
  }
18311
- 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
+ }))
18312
18360
  } catch (err) {
18313
- return errorMessageHandler(err, 'EOA')
18361
+ return errorMessageHandler(err, 'NEYNAR')
18314
18362
  }
18315
18363
  }
18316
18364
 
18317
- async function FLVURL(token, vs_currencies) {
18318
- return new Promise((resolve) => {
18319
- setTimeout(() => {
18320
- resolve([{ Yoo: 'gotcha' }]);
18321
- }, 10000);
18322
- })
18323
- }
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
+
18324
18378
 
18325
18379
  async function SAFE() {
18326
18380
  try {
@@ -18357,42 +18411,12 @@ async function SAFE() {
18357
18411
  }
18358
18412
  }
18359
18413
 
18360
-
18361
- async function DEFILLAMA() {
18362
- try {
18363
- const [category] = argsToArray(arguments);
18364
- validateParams(defillamaParamsSchema, { category });
18365
- const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Defillama);
18366
- const url = CATEGORY_URLS[category];
18367
- if (!url) throw new ValidationError(`Invalid category: ${category}`)
18368
- const res = await fetch(url);
18369
- if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Defillama, res.status)
18370
- let json = await res.json();
18371
-
18372
- switch (category) {
18373
- case 'protocols':
18374
- json = Array.isArray(json) ? json.slice(0, 500) : [];
18375
- break
18376
- case 'yields':
18377
- json = Array.isArray(json.data) ? json.data.slice(0, 500) : [];
18378
- break
18379
- case 'dex':
18380
- case 'fees':
18381
- json = Array.isArray(json.protocols) ? json.protocols.slice(0, 500) : [];
18382
- break
18383
- }
18384
-
18385
- return (Array.isArray(json) ? json : [json]).map(item => {
18386
- const out = {};
18387
- for (const [k, v] of Object.entries(item)) {
18388
- if (v === null || typeof v !== 'object') out[k] = v;
18389
- }
18390
- return out
18391
- })
18392
- } catch (err) {
18393
- return errorMessageHandler(err, 'DEFILLAMA')
18394
- }
18395
- }
18414
+ const uniswapParamsSchema = objectType({
18415
+ graphType: enumType(['v3','v3-raw']),
18416
+ category: enumType(['tokens','markets']),
18417
+ param1: stringType().nonempty(),
18418
+ param2: stringType().optional(),
18419
+ });
18396
18420
 
18397
18421
  async function UNISWAP() {
18398
18422
  try {
@@ -18432,43 +18456,12 @@ async function UNISWAP() {
18432
18456
  }
18433
18457
  }
18434
18458
 
18435
-
18436
- async function AAVE() {
18437
- try {
18438
-
18439
- const [graphType, category, param1, param2] = argsToArray(arguments);
18440
-
18441
-
18442
- validateParams(aaveParamsSchema, { graphType, category, param1, param2 });
18443
-
18444
- const baseUrl = 'https://onchain-proxy.fileverse.io/third-party';
18445
- const url =
18446
- `${baseUrl}` +
18447
- `?service=aave` +
18448
- `&graphType=${encodeURIComponent(graphType)}` +
18449
- `&category=${encodeURIComponent(category)}` +
18450
- `&input1=${encodeURIComponent(param1)}` +
18451
- (param2 ? `&input2=${encodeURIComponent(param2)}` : '');
18452
-
18453
- const res = await fetch(url);
18454
- if (!res.ok) {
18455
- throw new NetworkError('AAVE', res.status)
18456
- }
18457
-
18458
- const json = await res.json();
18459
- if (Array.isArray(json)) {
18460
- return json.map(item => {
18461
- const flat = {};
18462
- Object.entries(item).forEach(([k, v]) => {
18463
- if (v === null || typeof v !== 'object') flat[k] = v;
18464
- });
18465
- return flat
18466
- })
18467
- }
18468
- return json
18469
- } catch (err) {
18470
- return errorMessageHandler(err, 'AAVE')
18471
- }
18459
+ async function FLVURL() {
18460
+ return new Promise((resolve) => {
18461
+ setTimeout(() => {
18462
+ resolve([{ Yoo: 'gotcha' }]);
18463
+ }, 10000);
18464
+ })
18472
18465
  }
18473
18466
 
18474
18467
  function POLYMARKET() {