@fileverse-dev/formulajs 4.4.11-mod-77 → 4.4.11-mod-79

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,6 +13132,7 @@ const UTILITY = {
13132
13132
  };
13133
13133
  const MAX_PAGE_LIMIT = 250;
13134
13134
 
13135
+ // if data block need API key
13135
13136
  const SERVICES_API_KEY = {
13136
13137
  Etherscan: 'Etherscan',
13137
13138
  Coingecko: 'Coingecko',
@@ -13144,27 +13145,6 @@ const SERVICES_API_KEY = {
13144
13145
  Defillama: 'Defillama'
13145
13146
  };
13146
13147
 
13147
- const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
13148
- if(!timestamp || !chain || !apiKey) return
13149
- const chainId = CHAIN_ID_MAP[chain];
13150
- const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
13151
- const res = await fetch(url);
13152
- const json = await res.json();
13153
- return parseInt(json.result);
13154
-
13155
- };
13156
-
13157
- var fromTimestampToBlock = {
13158
- fromTimeStampToBlock
13159
- };
13160
-
13161
- function toTimestamp(dateStr) {
13162
- // Expecting format: "DD/MM/YYYY"
13163
- const [day, month, year] = dateStr.split("/").map(Number);
13164
- const date = new Date(year, month - 1, day);
13165
- return Math.floor(date.getTime() / 1000); // Unix timestamp in seconds
13166
- }
13167
-
13168
13148
  class ValidationError extends Error {
13169
13149
  constructor(message) {
13170
13150
  super(message);
@@ -13209,6 +13189,138 @@ class InvalidApiKeyError extends Error {
13209
13189
  }
13210
13190
  }
13211
13191
 
13192
+ const fileverseProxyUrl = `${process?.env?.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`;
13193
+ // Proxy map configuration
13194
+ const PROXY_MAP = {
13195
+ Etherscan: {
13196
+ url: fileverseProxyUrl,
13197
+ removeParams: ['apikey']
13198
+ },
13199
+ Basescan: {
13200
+ url: fileverseProxyUrl,
13201
+ removeParams: ['apikey']
13202
+ },
13203
+ Gnosisscan: {
13204
+ url: fileverseProxyUrl,
13205
+ removeParams: ['apikey']
13206
+ },
13207
+ Coingecko: {
13208
+ url: fileverseProxyUrl,
13209
+ removeParams: ['apikey']
13210
+ },
13211
+ Firefly: {
13212
+ url: fileverseProxyUrl,
13213
+ removeParams: ['apikey']
13214
+ },
13215
+ Neynar: {
13216
+ url: fileverseProxyUrl,
13217
+ removeParams: ['api_key']
13218
+ },
13219
+ Safe: {
13220
+ url: fileverseProxyUrl,
13221
+ removeParams: ['api_key']
13222
+ },
13223
+ Defillama: {
13224
+ url: fileverseProxyUrl,
13225
+ removeParams: ['api_key']
13226
+ },
13227
+ GnosisPay: {
13228
+ url: fileverseProxyUrl,
13229
+ removeParams: ['api_key']
13230
+ },
13231
+ // Add more services as needed. It can be direct url instead of ENV variable
13232
+ // ANOTHER_SERVICE: "https://another-proxy-url.com"
13233
+ };
13234
+
13235
+ /**
13236
+ * Removes specified parameters from a URL
13237
+ * @param {string} url - The original URL
13238
+ * @param {string[]} paramsToRemove - Array of parameter names to remove
13239
+ * @returns {string} URL with specified parameters removed
13240
+ */
13241
+ function removeUrlParams(url, paramsToRemove) {
13242
+ if (!paramsToRemove || paramsToRemove.length === 0) {
13243
+ return url;
13244
+ }
13245
+
13246
+ const urlObj = new URL(url);
13247
+
13248
+ paramsToRemove.forEach(param => {
13249
+ if (urlObj.searchParams.has(param)) {
13250
+ urlObj.searchParams.delete(param);
13251
+ }
13252
+ });
13253
+
13254
+ return urlObj.toString();
13255
+ }
13256
+
13257
+ /**
13258
+ * Handles URL routing through proxy or direct API calls
13259
+ * @param {string} url - The original API URL
13260
+ * @param {string} serviceName - [OPTIONAL] The name of the service (e.g., 'EOA')
13261
+ * @param {object} headers - [OPTIONAL] The name of the service (e.g., 'EOA')
13262
+ * @returns {Object} Object containing URL and HEADERS for the fetch request
13263
+ */
13264
+ function getUrlAndHeaders({ url, serviceName, headers = {} }) {
13265
+ // Check if proxy is enabled in localStorage
13266
+ const apiKeyLS = window.localStorage.getItem(SERVICES_API_KEY[serviceName]);
13267
+ const isProxyModeEnabledValue = apiKeyLS === 'DEFAULT_PROXY_MODE';
13268
+
13269
+ // Check if proxy URL exists for this service
13270
+ const proxyConfig = PROXY_MAP[serviceName];
13271
+
13272
+ if (!proxyConfig && SERVICES_API_KEY[serviceName] && (!apiKeyLS || apiKeyLS === '')) {
13273
+ throw new MissingApiKeyError(SERVICES_API_KEY[serviceName])
13274
+ }
13275
+
13276
+ // If proxy mode is enabled AND proxy URL exists for this service
13277
+ if ((isProxyModeEnabledValue || !apiKeyLS || apiKeyLS === '') && proxyConfig) {
13278
+ // Remove specified parameters from the target URL
13279
+ const cleanedUrl = removeUrlParams(url, proxyConfig.removeParams);
13280
+
13281
+ return {
13282
+ URL: proxyConfig.url,
13283
+ HEADERS: {
13284
+ 'target-url': cleanedUrl,
13285
+ method: 'GET',
13286
+ 'Content-Type': 'application/json'
13287
+ }
13288
+ };
13289
+ }
13290
+
13291
+ return {
13292
+ URL: url,
13293
+ HEADERS: {
13294
+ ...headers,
13295
+ }
13296
+ };
13297
+ }
13298
+
13299
+ const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
13300
+ if (!timestamp || !chain) return
13301
+ const chainId = CHAIN_ID_MAP[chain];
13302
+ const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime&timestamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
13303
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
13304
+ const res = await fetch(finalUrl, {
13305
+ method: 'GET',
13306
+ headers: HEADERS,
13307
+ });
13308
+ const json = await res.json();
13309
+ return parseInt(json.result);
13310
+
13311
+ };
13312
+
13313
+ var fromTimestampToBlock = {
13314
+ fromTimeStampToBlock
13315
+ };
13316
+
13317
+ function toTimestamp(dateStr) {
13318
+ // Expecting format: "DD/MM/YYYY"
13319
+ const [day, month, year] = dateStr.split("/").map(Number);
13320
+ const date = new Date(year, month - 1, day);
13321
+ return Math.floor(date.getTime() / 1000); // Unix timestamp in seconds
13322
+ }
13323
+
13212
13324
  const isAddress = (input) => {
13213
13325
  return (/^0x[a-fA-F0-9]{40}$/.test(input))
13214
13326
  };
@@ -13313,7 +13425,11 @@ async function handleScanRequest({
13313
13425
  }
13314
13426
  url += `&page=${page}&offset=${offset}`;
13315
13427
  }
13316
- const res = await fetch(url);
13428
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({url, serviceName: apiInfo.apiKeyName, headers: {}});
13429
+ const res = await fetch(finalUrl, {
13430
+ method: 'GET',
13431
+ headers: HEADERS,
13432
+ });
13317
13433
  if (!res.ok) {
13318
13434
  throw new NetworkError(apiInfo.apiKeyName, res.status)
13319
13435
  }
@@ -13332,14 +13448,19 @@ async function handleScanRequest({
13332
13448
  }
13333
13449
 
13334
13450
  const fromUsernameToFid = async (username, apiKey) => {
13335
- if(!username) return null
13451
+ if (!username) return null
13336
13452
  const url = `https://api.neynar.com/v2/farcaster/user/search/?q=${username}&limit=5`;
13337
- const res = await fetch(url, {
13338
- headers: {
13453
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
13454
+ url, serviceName: 'Neynar', headers: {
13339
13455
  'x-api-key': apiKey,
13340
13456
  'x-neynar-experimental': 'false'
13341
13457
  }
13342
13458
  });
13459
+
13460
+ const res = await fetch(finalUrl, {
13461
+ method: 'GET',
13462
+ headers: HEADERS,
13463
+ });
13343
13464
  const json = await res.json();
13344
13465
  const users = json.result ? json.result.users : [];
13345
13466
  const user = users.find(user => user.username === username);
@@ -17579,9 +17700,9 @@ const aaveParamsSchema = objectType({
17579
17700
 
17580
17701
  async function FIREFLY() {
17581
17702
  try {
17582
- const [platform, contentType, identifier, start = 0, end = 10] = argsToArray(arguments);
17703
+ const [platform, contentType, identifier, start = 0, end = 10] = argsToArray(arguments);
17583
17704
 
17584
- validateParams(fireflyParamsSchema, {
17705
+ validateParams(fireflyParamsSchema, {
17585
17706
  platform,
17586
17707
  contentType,
17587
17708
  identifier,
@@ -17590,9 +17711,6 @@ validateParams(fireflyParamsSchema, {
17590
17711
  });
17591
17712
 
17592
17713
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Firefly);
17593
- if (!apiKey) {
17594
- throw new MissingApiKeyError(SERVICES_API_KEY.Firefly)
17595
- }
17596
17714
 
17597
17715
  const url = new URL('https://openapi.firefly.land/v1/fileverse/fetch');
17598
17716
  url.searchParams
@@ -17603,12 +17721,14 @@ validateParams(fireflyParamsSchema, {
17603
17721
  .filter(Boolean)
17604
17722
  .join(',')
17605
17723
  );
17606
- url.searchParams.set('type', fireFlyPlaformType[platform][contentType]);
17607
- url.searchParams.set('start', String(start));
17608
- url.searchParams.set('end', String(end));
17724
+ url.searchParams.set('type', fireFlyPlaformType[platform][contentType]);
17725
+ url.searchParams.set('start', String(start));
17726
+ url.searchParams.set('end', String(end));
17609
17727
 
17610
- const response = await fetch(url.toString(), {
17611
- headers: { 'x-api-key': apiKey }
17728
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17729
+ const response = await fetch(finalUrl, {
17730
+ method: 'GET',
17731
+ headers: HEADERS,
17612
17732
  });
17613
17733
  if (!response.ok) {
17614
17734
  throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
@@ -17650,9 +17770,6 @@ async function LENS() {
17650
17770
  const apiKey = window.localStorage.getItem(
17651
17771
  SERVICES_API_KEY.Firefly
17652
17772
  );
17653
- if (!apiKey) {
17654
- throw new MissingApiKeyError(SERVICES_API_KEY.Firefly)
17655
- }
17656
17773
 
17657
17774
  const url = new URL(
17658
17775
  'https://openapi.firefly.land/v1/fileverse/fetch'
@@ -17666,15 +17783,18 @@ async function LENS() {
17666
17783
  .join(',')
17667
17784
  );
17668
17785
  const typeMap = {
17669
- posts: 'lensid',
17786
+ posts: 'lensid',
17670
17787
  replies: 'lenspostid',
17671
17788
  };
17672
17789
  url.searchParams.set('type', typeMap[contentType]);
17673
17790
  url.searchParams.set('start', String(start));
17674
- url.searchParams.set('end', String(end));
17791
+ url.searchParams.set('end', String(end));
17792
+
17793
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17675
17794
 
17676
- const response = await fetch(url.toString(), {
17677
- headers: { 'x-api-key': apiKey },
17795
+ const response = await fetch(finalUrl, {
17796
+ method: 'GET',
17797
+ headers: HEADERS,
17678
17798
  });
17679
17799
  if (!response.ok) {
17680
17800
  throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
@@ -17701,7 +17821,7 @@ async function FARCASTER() {
17701
17821
  try {
17702
17822
  const [contentType, identifier, start = 0, end = 10] =
17703
17823
  argsToArray(arguments);
17704
- validateParams(farcasterParamsSchema, {
17824
+ validateParams(farcasterParamsSchema, {
17705
17825
  contentType,
17706
17826
  identifier,
17707
17827
  start,
@@ -17711,9 +17831,6 @@ validateParams(farcasterParamsSchema, {
17711
17831
  const apiKey = window.localStorage.getItem(
17712
17832
  SERVICES_API_KEY.Firefly
17713
17833
  );
17714
- if (!apiKey) {
17715
- throw new MissingApiKeyError(SERVICES_API_KEY.Firefly)
17716
- }
17717
17834
 
17718
17835
  const url = new URL(
17719
17836
  'https://openapi.firefly.land/v1/fileverse/fetch'
@@ -17727,16 +17844,19 @@ validateParams(farcasterParamsSchema, {
17727
17844
  .join(',')
17728
17845
  );
17729
17846
  const typeMap = {
17730
- posts: 'farcasterid',
17731
- replies: 'farcasterpostid',
17732
- channels: 'farcasterchannels',
17733
- };
17847
+ posts: 'farcasterid',
17848
+ replies: 'farcasterpostid',
17849
+ channels: 'farcasterchannels',
17850
+ };
17734
17851
  url.searchParams.set('type', typeMap[contentType]);
17735
17852
  url.searchParams.set('start', String(start));
17736
- url.searchParams.set('end', String(end));
17853
+ url.searchParams.set('end', String(end));
17737
17854
 
17738
- const response = await fetch(url.toString(), {
17739
- headers: { 'x-api-key': apiKey },
17855
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url: url.toString(), serviceName: 'Firefly', headers: { 'x-api-key': apiKey } });
17856
+
17857
+ const response = await fetch(finalUrl, {
17858
+ method: 'GET',
17859
+ headers: HEADERS,
17740
17860
  });
17741
17861
  if (!response.ok) {
17742
17862
  throw new NetworkError(
@@ -17838,12 +17958,11 @@ async function BLOCKSCOUT() {
17838
17958
  }
17839
17959
 
17840
17960
  async function BASE() {
17841
- try {
17961
+ try {
17842
17962
  const [type, address, startDate, endDate, page, limit] = argsToArray(arguments);
17843
- validateParams(baseParamsSchema, { type, address, startDate, endDate, page, limit });
17963
+ validateParams(baseParamsSchema, { type, address, startDate, endDate, page, limit });
17844
17964
  const API_KEY = window.localStorage.getItem(SERVICES_API_KEY.Basescan);
17845
- if (!API_KEY) throw new MissingApiKeyError(SERVICES_API_KEY.Basescan)
17846
-
17965
+
17847
17966
  return await handleScanRequest({
17848
17967
  type,
17849
17968
  address,
@@ -17856,9 +17975,9 @@ try {
17856
17975
  chainId: CHAIN_ID_MAP.base,
17857
17976
  network: 'base'
17858
17977
  })
17859
- } catch (error) {
17860
- return errorMessageHandler(error, 'BASE')
17861
- }
17978
+ } catch (error) {
17979
+ return errorMessageHandler(error, 'BASE')
17980
+ }
17862
17981
  }
17863
17982
  async function GNOSIS() {
17864
17983
  try {
@@ -17878,7 +17997,6 @@ async function GNOSIS() {
17878
17997
  const apiKey = window.localStorage.getItem(
17879
17998
  SERVICES_API_KEY.Gnosisscan
17880
17999
  );
17881
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Gnosisscan)
17882
18000
 
17883
18001
  return await handleScanRequest({
17884
18002
  type,
@@ -17899,27 +18017,33 @@ async function GNOSIS() {
17899
18017
 
17900
18018
  async function NEYNAR() {
17901
18019
  try {
17902
- const neynarParamsSchema = objectType({
17903
- username: stringType().nonempty()
17904
- });
18020
+ const neynarParamsSchema = objectType({
18021
+ username: stringType().nonempty()
18022
+ });
17905
18023
 
17906
18024
  const [username] = argsToArray(arguments);
17907
18025
 
17908
18026
  validateParams(neynarParamsSchema, { username });
17909
18027
 
17910
18028
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Neynar);
17911
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Neynar)
17912
18029
 
17913
18030
  const fid = await fromUsernameToFid$1.fromUsernameToFid(username, apiKey);
17914
18031
  if (!fid) throw new ValidationError(`Invalid username: ${username}`)
17915
18032
 
17916
18033
  const url = `https://api.neynar.com/v2/farcaster/followers?fid=${fid}`;
17917
18034
 
17918
- const response = await fetch(url, {
17919
- headers: {
17920
- 'x-api-key': apiKey,
17921
- 'x-neynar-experimental': 'false',
17922
- }
18035
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
18036
+ url: url.toString(), serviceName: 'Neynar',
18037
+ headers: {
18038
+ 'x-api-key': apiKey,
18039
+ 'x-neynar-experimental': 'false'
18040
+ }
18041
+
18042
+ });
18043
+
18044
+ const response = await fetch(finalUrl, {
18045
+ method: 'GET',
18046
+ headers: HEADERS,
17923
18047
  });
17924
18048
  if (!response.ok) {
17925
18049
  throw new NetworkError(SERVICES_API_KEY.Neynar, response.status)
@@ -18014,7 +18138,6 @@ async function ETHERSCAN() {
18014
18138
  if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
18015
18139
 
18016
18140
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
18017
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Etherscan)
18018
18141
 
18019
18142
  return await handleScanRequest({
18020
18143
  type,
@@ -18040,7 +18163,6 @@ async function COINGECKO() {
18040
18163
  validateParams(coingeckoParamsSchema, { category, param1, param2 });
18041
18164
 
18042
18165
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
18043
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Coingecko)
18044
18166
 
18045
18167
  const headers = {
18046
18168
  accept: 'application/json',
@@ -18054,27 +18176,28 @@ async function COINGECKO() {
18054
18176
  break
18055
18177
  }
18056
18178
  case 'market': {
18057
- 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' };
18179
+ 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' };
18058
18180
  const _category = map[param1] || '';
18059
18181
  const trend = param2 ? `&price_change_percentage=${param2}` : '';
18060
- url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category?`&category=${_category}`:''}${trend}`;
18182
+ url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category ? `&category=${_category}` : ''}${trend}`;
18061
18183
  break
18062
18184
  }
18063
18185
  case 'stablecoins': {
18064
- const _category = param1==='all'? 'stablecoins' : param1;
18186
+ const _category = param1 === 'all' ? 'stablecoins' : param1;
18065
18187
  const trend = param2 ? `&price_change_percentage=${param2}` : '';
18066
18188
  url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=${_category}&order=market_cap_desc&page=1&per_page=100${trend}`;
18067
18189
  break
18068
18190
  }
18069
18191
  case 'derivatives': {
18070
- url = (!param1 || param1==='all')
18192
+ url = (!param1 || param1 === 'all')
18071
18193
  ? 'https://api.coingecko.com/api/v3/derivatives'
18072
18194
  : `https://api.coingecko.com/api/v3/derivatives/exchanges/${param1}?include_tickers=all`;
18073
18195
  break
18074
18196
  }
18075
18197
  }
18198
+ const {URL: finalUrl, HEADERS} = getUrlAndHeaders({url, serviceName: 'Coingecko', headers});
18076
18199
 
18077
- const res = await fetch(url, { headers });
18200
+ const res = await fetch(finalUrl, { headers: HEADERS });
18078
18201
  const json = await res.json();
18079
18202
  if (!res.ok) {
18080
18203
  const msg = json?.status?.error_message || '';
@@ -18082,17 +18205,17 @@ async function COINGECKO() {
18082
18205
  throw new NetworkError(SERVICES_API_KEY.Coingecko, res.status)
18083
18206
  }
18084
18207
 
18085
- if (category==='price') {
18208
+ if (category === 'price') {
18086
18209
  const out = {};
18087
18210
  for (const [token, prices] of Object.entries(json))
18088
- for (const [cur,val] of Object.entries(prices))
18089
- out[`${token.charAt(0).toUpperCase()+token.slice(1)}_${cur.toUpperCase()}`]=val;
18211
+ for (const [cur, val] of Object.entries(prices))
18212
+ out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
18090
18213
  return [out]
18091
18214
  }
18092
18215
 
18093
18216
  const data = Array.isArray(json) ? json : [json];
18094
- return data.map(item=>{
18095
- const flat={};
18217
+ return data.map(item => {
18218
+ const flat = {};
18096
18219
  for (const [key, value] of Object.entries(item)) {
18097
18220
  if (typeof value !== 'object' || value === null) {
18098
18221
  flat[key] = value;
@@ -18100,8 +18223,8 @@ async function COINGECKO() {
18100
18223
  }
18101
18224
  return flat
18102
18225
  })
18103
- } catch(err) {
18104
- return errorMessageHandler(err,'COINGECKO')
18226
+ } catch (err) {
18227
+ return errorMessageHandler(err, 'COINGECKO')
18105
18228
  }
18106
18229
  }
18107
18230
 
@@ -18112,10 +18235,9 @@ async function EOA() {
18112
18235
  validateParams(eoaParamsSchema, { addresses, category, chains, startTime, endTime, page, offset });
18113
18236
 
18114
18237
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
18115
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Etherscan)
18116
18238
 
18117
- const INPUTS = addresses.split(',').map(s=>s.trim()).filter(Boolean);
18118
- const CHAINS = chains.split(',').map(s=>s.trim()).filter(Boolean);
18239
+ const INPUTS = addresses.split(',').map(s => s.trim()).filter(Boolean);
18240
+ const CHAINS = chains.split(',').map(s => s.trim()).filter(Boolean);
18119
18241
 
18120
18242
  const ADDRESS_MAP = {};
18121
18243
  for (const inp of INPUTS) {
@@ -18130,7 +18252,11 @@ async function EOA() {
18130
18252
  const out = [];
18131
18253
 
18132
18254
  async function fetchJSON(url) {
18133
- const res = await fetch(url);
18255
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
18256
+ const res = await fetch(finalUrl, {
18257
+ method: 'GET',
18258
+ headers: HEADERS,
18259
+ });
18134
18260
  if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Etherscan, res.status)
18135
18261
  const json = await res.json();
18136
18262
 
@@ -18148,15 +18274,15 @@ async function EOA() {
18148
18274
 
18149
18275
  if (category === 'balance') {
18150
18276
  // chunk 20
18151
- for (let i=0; i<ADDRS.length; i+=20) {
18152
- const slice = ADDRS.slice(i,i+20).join(',');
18277
+ for (let i = 0; i < ADDRS.length; i += 20) {
18278
+ const slice = ADDRS.slice(i, i + 20).join(',');
18153
18279
  const url =
18154
- `https://api.etherscan.io/v2/api?chainid=${chainId}`+
18155
- `&module=account&action=addresstokenbalance&address=${slice}`+
18280
+ `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18281
+ `&module=account&action=addresstokenbalance&address=${slice}` +
18156
18282
  `&page=${page}&offset=${offset}&apikey=${apiKey}`;
18157
18283
  const data = await fetchJSON(url);
18158
18284
  if (!Array.isArray(data)) return data
18159
- data.forEach((item, idx) => out.push({ chain, address: ADDRS[i+idx], name: ADDRESS_MAP[ADDRS[i+idx]], ...item }));
18285
+ data.forEach((item, idx) => out.push({ chain, address: ADDRS[i + idx], name: ADDRESS_MAP[ADDRS[i + idx]], ...item }));
18160
18286
  }
18161
18287
  } else {
18162
18288
  // txns
@@ -18166,9 +18292,9 @@ async function EOA() {
18166
18292
  if (!eb) throw new ValidationError(`Invalid endTime: ${endTime}`)
18167
18293
  for (const addr of ADDRS) {
18168
18294
  const url =
18169
- `https://api.etherscan.io/v2/api?chainid=${chainId}`+
18170
- `&module=account&action=tokentx&address=${addr}`+
18171
- `&startblock=${sb}&endblock=${eb}`+
18295
+ `https://api.etherscan.io/v2/api?chainid=${chainId}` +
18296
+ `&module=account&action=tokentx&address=${addr}` +
18297
+ `&startblock=${sb}&endblock=${eb}` +
18172
18298
  `&page=${page}&offset=${offset}&sort=asc&apikey=${apiKey}`;
18173
18299
  const data = await fetchJSON(url);
18174
18300
  if (!Array.isArray(data)) return data
@@ -18197,7 +18323,6 @@ async function SAFE() {
18197
18323
  validateParams(safeParamsSchema, { address, utility, chain, limit, offset });
18198
18324
 
18199
18325
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Safe);
18200
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Safe)
18201
18326
 
18202
18327
  const chainId = SAFE_CHAIN_MAP[chain];
18203
18328
  if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
@@ -18210,8 +18335,8 @@ async function SAFE() {
18210
18335
 
18211
18336
  const url = `https://api.safe.global/tx-service/${chainId}/api/v2/safes/${resolved}/multisig-transactions?limit=${limit}&offset=${offset}`;
18212
18337
 
18213
-
18214
- const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
18338
+ const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: { Authorization: `Bearer ${apiKey}` } });
18339
+ const res = await fetch(finalUrl, { headers: HEADERS });
18215
18340
  if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Safe, res.status)
18216
18341
  const json = await res.json();
18217
18342
 
@@ -18232,7 +18357,6 @@ async function DEFILLAMA() {
18232
18357
  const [category] = argsToArray(arguments);
18233
18358
  validateParams(defillamaParamsSchema, { category });
18234
18359
  const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Defillama);
18235
- if (!apiKey) throw new MissingApiKeyError(SERVICES_API_KEY.Defillama)
18236
18360
  const url = CATEGORY_URLS[category];
18237
18361
  if (!url) throw new ValidationError(`Invalid category: ${category}`)
18238
18362
  const res = await fetch(url);
@@ -18241,20 +18365,20 @@ async function DEFILLAMA() {
18241
18365
 
18242
18366
  switch (category) {
18243
18367
  case 'protocols':
18244
- json = Array.isArray(json) ? json.slice(0,500) : [];
18368
+ json = Array.isArray(json) ? json.slice(0, 500) : [];
18245
18369
  break
18246
18370
  case 'yields':
18247
- json = Array.isArray(json.data) ? json.data.slice(0,500) : [];
18371
+ json = Array.isArray(json.data) ? json.data.slice(0, 500) : [];
18248
18372
  break
18249
18373
  case 'dex':
18250
18374
  case 'fees':
18251
- json = Array.isArray(json.protocols) ? json.protocols.slice(0,500) : [];
18375
+ json = Array.isArray(json.protocols) ? json.protocols.slice(0, 500) : [];
18252
18376
  break
18253
18377
  }
18254
18378
 
18255
18379
  return (Array.isArray(json) ? json : [json]).map(item => {
18256
18380
  const out = {};
18257
- for (const [k,v] of Object.entries(item)) {
18381
+ for (const [k, v] of Object.entries(item)) {
18258
18382
  if (v === null || typeof v !== 'object') out[k] = v;
18259
18383
  }
18260
18384
  return out
@@ -18290,7 +18414,7 @@ async function UNISWAP() {
18290
18414
  // flatten nested
18291
18415
  return json.map(item => {
18292
18416
  const flat = {};
18293
- Object.entries(item).forEach(([k,v]) => {
18417
+ Object.entries(item).forEach(([k, v]) => {
18294
18418
  if (v === null || typeof v !== 'object') flat[k] = v;
18295
18419
  });
18296
18420
  return flat