@fileverse-dev/formulajs 4.4.11-mod-83 → 4.4.11-mod-83-patch-2

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