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