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