@fileverse-dev/formulajs 4.4.11-mod-82 → 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 +1856 -1858
- package/lib/browser/formula.min.js +2 -2
- package/lib/browser/formula.min.js.map +1 -1
- package/lib/cjs/index.cjs +992 -1043
- package/lib/esm/crypto-constants.mjs +655 -859
- package/lib/esm/index.mjs +992 -1043
- 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,419 +13188,86 @@ class InvalidApiKeyError extends Error {
|
|
|
13189
13188
|
}
|
|
13190
13189
|
}
|
|
13191
13190
|
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13202
|
-
removeParams: ['apikey']
|
|
13203
|
-
},
|
|
13204
|
-
Basescan: {
|
|
13205
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13206
|
-
removeParams: ['apikey']
|
|
13207
|
-
},
|
|
13208
|
-
Gnosisscan: {
|
|
13209
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13210
|
-
removeParams: ['apikey']
|
|
13211
|
-
},
|
|
13212
|
-
Coingecko: {
|
|
13213
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13214
|
-
removeParams: ['apikey']
|
|
13215
|
-
},
|
|
13216
|
-
Firefly: {
|
|
13217
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13218
|
-
removeParams: ['apikey']
|
|
13219
|
-
},
|
|
13220
|
-
Neynar: {
|
|
13221
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13222
|
-
removeParams: ['api_key']
|
|
13223
|
-
},
|
|
13224
|
-
Safe: {
|
|
13225
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13226
|
-
removeParams: ['api_key']
|
|
13227
|
-
},
|
|
13228
|
-
Defillama: {
|
|
13229
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13230
|
-
removeParams: ['api_key']
|
|
13231
|
-
},
|
|
13232
|
-
GnosisPay: {
|
|
13233
|
-
url: `${fileverseProxyUrl}/proxy`,
|
|
13234
|
-
removeParams: ['api_key']
|
|
13235
|
-
},
|
|
13236
|
-
// Add more services as needed. It can be direct url instead of ENV variable
|
|
13237
|
-
// ANOTHER_SERVICE: "https://another-proxy-url.com"
|
|
13238
|
-
};
|
|
13239
|
-
}
|
|
13240
|
-
return PROXY_MAP;
|
|
13241
|
-
}
|
|
13242
|
-
|
|
13243
|
-
// const fileverseProxyUrl = `${window.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`
|
|
13244
|
-
// // Proxy map configuration
|
|
13245
|
-
// const PROXY_MAP = {
|
|
13246
|
-
// Etherscan: {
|
|
13247
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13248
|
-
// removeParams: ['apikey']
|
|
13249
|
-
// },
|
|
13250
|
-
// Basescan: {
|
|
13251
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13252
|
-
// removeParams: ['apikey']
|
|
13253
|
-
// },
|
|
13254
|
-
// Gnosisscan: {
|
|
13255
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13256
|
-
// removeParams: ['apikey']
|
|
13257
|
-
// },
|
|
13258
|
-
// Coingecko: {
|
|
13259
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13260
|
-
// removeParams: ['apikey']
|
|
13261
|
-
// },
|
|
13262
|
-
// Firefly: {
|
|
13263
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13264
|
-
// removeParams: ['apikey']
|
|
13265
|
-
// },
|
|
13266
|
-
// Neynar: {
|
|
13267
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13268
|
-
// removeParams: ['api_key']
|
|
13269
|
-
// },
|
|
13270
|
-
// Safe: {
|
|
13271
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13272
|
-
// removeParams: ['api_key']
|
|
13273
|
-
// },
|
|
13274
|
-
// Defillama: {
|
|
13275
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13276
|
-
// removeParams: ['api_key']
|
|
13277
|
-
// },
|
|
13278
|
-
// GnosisPay: {
|
|
13279
|
-
// url: `${process.env.NEXT_PUBLIC_PROXY_BASE_URL}/proxy`,
|
|
13280
|
-
// removeParams: ['api_key']
|
|
13281
|
-
// },
|
|
13282
|
-
// // Add more services as needed. It can be direct url instead of ENV variable
|
|
13283
|
-
// // ANOTHER_SERVICE: "https://another-proxy-url.com"
|
|
13284
|
-
// };
|
|
13285
|
-
|
|
13286
|
-
/**
|
|
13287
|
-
* Removes specified parameters from a URL
|
|
13288
|
-
* @param {string} url - The original URL
|
|
13289
|
-
* @param {string[]} paramsToRemove - Array of parameter names to remove
|
|
13290
|
-
* @returns {string} URL with specified parameters removed
|
|
13291
|
-
*/
|
|
13292
|
-
function removeUrlParams(url, paramsToRemove) {
|
|
13293
|
-
if (!paramsToRemove || paramsToRemove.length === 0) {
|
|
13294
|
-
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
|
+
}
|
|
13295
13200
|
}
|
|
13296
13201
|
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
});
|
|
13304
|
-
|
|
13305
|
-
return urlObj.toString();
|
|
13306
|
-
}
|
|
13307
|
-
|
|
13308
|
-
/**
|
|
13309
|
-
* Handles URL routing through proxy or direct API calls
|
|
13310
|
-
* @param {string} url - The original API URL
|
|
13311
|
-
* @param {string} serviceName - [OPTIONAL] The name of the service (e.g., 'EOA')
|
|
13312
|
-
* @param {object} headers - [OPTIONAL] The name of the service (e.g., 'EOA')
|
|
13313
|
-
* @returns {Object} Object containing URL and HEADERS for the fetch request
|
|
13314
|
-
*/
|
|
13315
|
-
function getUrlAndHeaders({ url, serviceName, headers = {} }) {
|
|
13316
|
-
const proxyMap = initializeProxyMap();
|
|
13317
|
-
// Check if proxy is enabled in localStorage
|
|
13318
|
-
const apiKeyLS = window.localStorage.getItem(SERVICES_API_KEY[serviceName]);
|
|
13319
|
-
const isProxyModeEnabledValue = apiKeyLS === 'DEFAULT_PROXY_MODE';
|
|
13320
|
-
|
|
13321
|
-
// Check if proxy URL exists for this service
|
|
13322
|
-
const proxyConfig = proxyMap[serviceName];
|
|
13323
|
-
|
|
13324
|
-
if (!proxyConfig && SERVICES_API_KEY[serviceName] && (!apiKeyLS || apiKeyLS === '')) {
|
|
13325
|
-
throw new MissingApiKeyError(SERVICES_API_KEY[serviceName])
|
|
13326
|
-
}
|
|
13202
|
+
case err instanceof MissingApiKeyError :
|
|
13203
|
+
return {
|
|
13204
|
+
message: err.message,
|
|
13205
|
+
functionName,
|
|
13206
|
+
type: ERROR_MESSAGES_FLAG.MISSING_KEY
|
|
13207
|
+
}
|
|
13327
13208
|
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
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
|
+
}
|
|
13332
13216
|
|
|
13217
|
+
case err instanceof NetworkError:
|
|
13218
|
+
if(err.status === 429){
|
|
13333
13219
|
return {
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
}
|
|
13348
|
-
};
|
|
13349
|
-
}
|
|
13350
|
-
|
|
13351
|
-
const fromTimeStampToBlock = async (timestamp, chain, apiKey) => {
|
|
13352
|
-
if (!timestamp || !chain) return
|
|
13353
|
-
const chainId = CHAIN_ID_MAP[chain];
|
|
13354
|
-
const url = `https://api.etherscan.io/v2/api?module=block&action=getblocknobytime×tamp=${timestamp}&closest=before&apikey=${apiKey}&chainId=${chainId}`;
|
|
13355
|
-
const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
|
|
13356
|
-
const res = await fetch(finalUrl, {
|
|
13357
|
-
method: 'GET',
|
|
13358
|
-
headers: HEADERS,
|
|
13359
|
-
});
|
|
13360
|
-
const json = await res.json();
|
|
13361
|
-
return parseInt(json.result);
|
|
13362
|
-
|
|
13363
|
-
};
|
|
13364
|
-
|
|
13365
|
-
var fromTimestampToBlock = {
|
|
13366
|
-
fromTimeStampToBlock
|
|
13367
|
-
};
|
|
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
|
+
}
|
|
13368
13233
|
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
}
|
|
13234
|
+
case err instanceof EnsError:
|
|
13235
|
+
return {
|
|
13236
|
+
message: err.message,
|
|
13237
|
+
functionName,
|
|
13238
|
+
type: ERROR_MESSAGES_FLAG.ENS
|
|
13239
|
+
}
|
|
13375
13240
|
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
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
|
+
}
|
|
13379
13248
|
|
|
13380
|
-
|
|
13381
|
-
|
|
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
|
+
}
|
|
13382
13257
|
};
|
|
13383
13258
|
|
|
13384
|
-
/* global document */
|
|
13385
|
-
/* global window */
|
|
13386
|
-
/* global ethers */
|
|
13387
|
-
|
|
13388
13259
|
|
|
13389
13260
|
|
|
13390
|
-
async function fromEnsNameToAddress(name) {
|
|
13391
|
-
if (typeof ethers === 'undefined') {
|
|
13392
|
-
await new Promise((resolve, reject) => {
|
|
13393
|
-
const script = document.createElement('script');
|
|
13394
|
-
script.src = 'https://cdn.jsdelivr.net/npm/ethers@6.10.0/dist/ethers.umd.min.js';
|
|
13395
|
-
script.onload = resolve;
|
|
13396
|
-
script.onerror = reject;
|
|
13397
|
-
document.head.appendChild(script);
|
|
13398
|
-
});
|
|
13399
|
-
}
|
|
13400
|
-
|
|
13401
|
-
const ALCHEMY_KEY = window.localStorage.getItem(UTILITY.ALCHEMY_API_KEY);
|
|
13402
|
-
if (!ALCHEMY_KEY) {
|
|
13403
|
-
console.error('alchemy api key missing');
|
|
13404
|
-
return null
|
|
13405
|
-
}
|
|
13406
|
-
const provider = new ethers.AlchemyProvider('mainnet', ALCHEMY_KEY);
|
|
13407
13261
|
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
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
|
+
)
|
|
13414
13269
|
}
|
|
13415
|
-
|
|
13416
|
-
const validateAndGetAddress = async (address) => {
|
|
13417
|
-
if(isAddress$1.isAddress(address)) return address
|
|
13418
|
-
|
|
13419
|
-
const resolvedAddress = await fromEnsNameToAddress(address);
|
|
13420
|
-
if(resolvedAddress) return resolvedAddress
|
|
13421
|
-
throw new ValidationError("Invalid address")
|
|
13422
|
-
};
|
|
13423
|
-
|
|
13424
|
-
var fromEnsNameToAddress$1 = {
|
|
13425
|
-
validateAndGetAddress
|
|
13426
|
-
};
|
|
13427
|
-
|
|
13428
|
-
async function handleScanRequest({
|
|
13429
|
-
type,
|
|
13430
|
-
address,
|
|
13431
|
-
startDate,
|
|
13432
|
-
endDate,
|
|
13433
|
-
page = 1,
|
|
13434
|
-
offset = 10,
|
|
13435
|
-
apiKey,
|
|
13436
|
-
functionName,
|
|
13437
|
-
chainId,
|
|
13438
|
-
network
|
|
13439
|
-
}) {
|
|
13440
|
-
const API_INFO_MAP = {
|
|
13441
|
-
BASE: { url: 'https://api.basescan.org/api', apiKeyName: SERVICES_API_KEY.Basescan },
|
|
13442
|
-
ETHERSCAN: { url: 'https://api.etherscan.io/v2/api', apiKeyName: SERVICES_API_KEY.Etherscan },
|
|
13443
|
-
GNOSIS: { url: 'https://api.gnosisscan.io/api', apiKeyName: SERVICES_API_KEY.Gnosisscan }
|
|
13444
|
-
};
|
|
13445
|
-
|
|
13446
|
-
if (type !== 'gas') {
|
|
13447
|
-
address = await fromEnsNameToAddress$1.validateAndGetAddress(address);
|
|
13448
|
-
}
|
|
13449
|
-
|
|
13450
|
-
const apiInfo = API_INFO_MAP[functionName];
|
|
13451
|
-
const baseUrl = apiInfo?.url;
|
|
13452
|
-
if (!baseUrl) throw new ValidationError(`Api not found for: ${functionName}`)
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
const ACTION_MAP = {
|
|
13456
|
-
'all-txns': 'txlist',
|
|
13457
|
-
'token-txns': 'tokentx',
|
|
13458
|
-
'nft-txns': 'tokennfttx',
|
|
13459
|
-
'gas': 'gasoracle'
|
|
13460
|
-
};
|
|
13461
|
-
|
|
13462
|
-
const action = ACTION_MAP[type];
|
|
13463
|
-
if (!action) throw new ValidationError(`Invalid type: ${type}`)
|
|
13464
|
-
|
|
13465
|
-
const module = action === 'gasoracle' ? 'gastracker' : 'account';
|
|
13466
|
-
let url = `${baseUrl}?chainid=${chainId}&module=${module}&action=${action}&apikey=${apiKey}`;
|
|
13467
|
-
|
|
13468
|
-
if (['all-txns', 'token-txns', 'nft-txns'].includes(type)) {
|
|
13469
|
-
url += `&address=${address}&startblock=0&endblock=99999999&sort=asc`;
|
|
13470
|
-
|
|
13471
|
-
if (!isNaN(startDate) && !isNaN(endDate)) {
|
|
13472
|
-
const [startBlock, endBlock] = await Promise.all([
|
|
13473
|
-
fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(startDate), network, apiKey),
|
|
13474
|
-
fromTimestampToBlock.fromTimeStampToBlock(toTimestamp(endDate), network, apiKey)
|
|
13475
|
-
]);
|
|
13476
|
-
url += `&startblock=${startBlock || '0'}&endblock=${endBlock || '99999999'}`;
|
|
13477
|
-
}
|
|
13478
|
-
url += `&page=${page}&offset=${offset}`;
|
|
13479
|
-
}
|
|
13480
|
-
const { URL: finalUrl, HEADERS } = getUrlAndHeaders({url, serviceName: apiInfo.apiKeyName, headers: {}});
|
|
13481
|
-
const res = await fetch(finalUrl, {
|
|
13482
|
-
method: 'GET',
|
|
13483
|
-
headers: HEADERS,
|
|
13484
|
-
});
|
|
13485
|
-
if (!res.ok) {
|
|
13486
|
-
throw new NetworkError(apiInfo.apiKeyName, res.status)
|
|
13487
|
-
}
|
|
13488
|
-
const json = await res.json();
|
|
13489
|
-
|
|
13490
|
-
if (typeof json.result === 'string') {
|
|
13491
|
-
if (json.result.includes('Invalid API Key')){
|
|
13492
|
-
throw new InvalidApiKeyError(apiInfo.apiKeyName)
|
|
13493
|
-
}
|
|
13494
|
-
|
|
13495
|
-
if (json.result.includes('Max rate limit reached'))
|
|
13496
|
-
throw new RateLimitError(apiInfo.apiKeyName)
|
|
13497
|
-
}
|
|
13498
|
-
|
|
13499
|
-
return type === 'gas' && !Array.isArray(json.result) ? [json.result] : json.result
|
|
13500
|
-
}
|
|
13501
|
-
|
|
13502
|
-
const fromUsernameToFid = async (username, apiKey) => {
|
|
13503
|
-
if (!username) return null
|
|
13504
|
-
const url = `https://api.neynar.com/v2/farcaster/user/search/?q=${username}&limit=5`;
|
|
13505
|
-
const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
|
|
13506
|
-
url, serviceName: 'Neynar', headers: {
|
|
13507
|
-
'x-api-key': apiKey,
|
|
13508
|
-
'x-neynar-experimental': 'false'
|
|
13509
|
-
}
|
|
13510
|
-
});
|
|
13511
|
-
|
|
13512
|
-
const res = await fetch(finalUrl, {
|
|
13513
|
-
method: 'GET',
|
|
13514
|
-
headers: HEADERS,
|
|
13515
|
-
});
|
|
13516
|
-
const json = await res.json();
|
|
13517
|
-
const users = json.result ? json.result.users : [];
|
|
13518
|
-
const user = users.find(user => user.username === username);
|
|
13519
|
-
return user && user.fid || null;
|
|
13520
|
-
};
|
|
13521
|
-
var fromUsernameToFid$1 = {
|
|
13522
|
-
fromUsernameToFid
|
|
13523
|
-
};
|
|
13524
|
-
|
|
13525
|
-
const errorMessageHandler = (err, functionName) => {
|
|
13526
|
-
|
|
13527
|
-
switch (true) {
|
|
13528
|
-
case err instanceof ValidationError : {
|
|
13529
|
-
return {
|
|
13530
|
-
message: err.message,
|
|
13531
|
-
functionName,
|
|
13532
|
-
type: ERROR_MESSAGES_FLAG.INVALID_PARAM
|
|
13533
|
-
}
|
|
13534
|
-
}
|
|
13535
|
-
|
|
13536
|
-
case err instanceof MissingApiKeyError :
|
|
13537
|
-
return {
|
|
13538
|
-
message: err.message,
|
|
13539
|
-
functionName,
|
|
13540
|
-
type: ERROR_MESSAGES_FLAG.MISSING_KEY
|
|
13541
|
-
}
|
|
13542
|
-
|
|
13543
|
-
case err instanceof RateLimitError:
|
|
13544
|
-
return {
|
|
13545
|
-
message: err.message,
|
|
13546
|
-
functionName,
|
|
13547
|
-
type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
|
|
13548
|
-
apiKeyName: err.api
|
|
13549
|
-
}
|
|
13550
|
-
|
|
13551
|
-
case err instanceof NetworkError:
|
|
13552
|
-
if(err.status === 429){
|
|
13553
|
-
return {
|
|
13554
|
-
message: err.message,
|
|
13555
|
-
functionName,
|
|
13556
|
-
type: ERROR_MESSAGES_FLAG.RATE_LIMIT,
|
|
13557
|
-
apiKeyName: err.api
|
|
13558
|
-
}
|
|
13559
|
-
} else {
|
|
13560
|
-
return {
|
|
13561
|
-
message: err.message,
|
|
13562
|
-
functionName,
|
|
13563
|
-
type: ERROR_MESSAGES_FLAG.NETWORK_ERROR,
|
|
13564
|
-
apiKeyName: err.api
|
|
13565
|
-
}
|
|
13566
|
-
}
|
|
13567
|
-
|
|
13568
|
-
case err instanceof EnsError:
|
|
13569
|
-
return {
|
|
13570
|
-
message: err.message,
|
|
13571
|
-
functionName,
|
|
13572
|
-
type: ERROR_MESSAGES_FLAG.ENS
|
|
13573
|
-
}
|
|
13574
|
-
|
|
13575
|
-
case err instanceof InvalidApiKeyError:
|
|
13576
|
-
return {
|
|
13577
|
-
message: err.message,
|
|
13578
|
-
functionName,
|
|
13579
|
-
type: ERROR_MESSAGES_FLAG.INVALID_API_KEY,
|
|
13580
|
-
apiKeyName: err.api
|
|
13581
|
-
}
|
|
13582
|
-
|
|
13583
|
-
default:
|
|
13584
|
-
return {
|
|
13585
|
-
message: 'An unexpected error occured',
|
|
13586
|
-
functionName,
|
|
13587
|
-
type: ERROR_MESSAGES_FLAG.DEFAULT,
|
|
13588
|
-
reason: err.message || err
|
|
13589
|
-
}
|
|
13590
|
-
}
|
|
13591
|
-
};
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
function validateParams(schema, rawParams) {
|
|
13597
|
-
const result = schema.safeParse(rawParams);
|
|
13598
|
-
if (!result.success) {
|
|
13599
|
-
const issue = result.error.issues[0];
|
|
13600
|
-
throw new ValidationError(
|
|
13601
|
-
issue.message
|
|
13602
|
-
)
|
|
13603
|
-
}
|
|
13604
|
-
return result.data
|
|
13270
|
+
return result.data
|
|
13605
13271
|
}
|
|
13606
13272
|
|
|
13607
13273
|
var util;
|
|
@@ -17520,51 +17186,50 @@ ZodOptional.create;
|
|
|
17520
17186
|
ZodNullable.create;
|
|
17521
17187
|
const preprocessType = ZodEffects.createWithPreprocess;
|
|
17522
17188
|
|
|
17523
|
-
const
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
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(),
|
|
17529
17194
|
});
|
|
17530
17195
|
|
|
17531
|
-
|
|
17532
|
-
|
|
17533
|
-
contentType: enumType(['posts', 'replies']),
|
|
17534
|
-
identifier: stringType().nonempty(),
|
|
17535
|
-
start: numberType().int().nonnegative().default(0),
|
|
17536
|
-
end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17537
|
-
});
|
|
17196
|
+
async function AAVE() {
|
|
17197
|
+
try {
|
|
17538
17198
|
|
|
17539
|
-
const
|
|
17540
|
-
farcasterSchema,
|
|
17541
|
-
lensSchema,
|
|
17542
|
-
]);
|
|
17543
|
-
const fireFlyPlaformType = {
|
|
17544
|
-
farcaster: {
|
|
17545
|
-
posts: 'farcasterid',
|
|
17546
|
-
replies: 'farcasterpostid',
|
|
17547
|
-
channels: 'farcasterchannels'
|
|
17548
|
-
},
|
|
17549
|
-
lens: {
|
|
17550
|
-
posts: 'lensid',
|
|
17551
|
-
replies: 'lenspostid'
|
|
17552
|
-
}
|
|
17553
|
-
};
|
|
17199
|
+
const [graphType, category, param1, param2] = argsToArray(arguments);
|
|
17554
17200
|
|
|
17555
|
-
const lensParamsSchema = objectType({
|
|
17556
|
-
contentType: enumType(['posts', 'replies']),
|
|
17557
|
-
identifier: stringType().nonempty(),
|
|
17558
|
-
start: numberType().int().nonnegative().default(0),
|
|
17559
|
-
end: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"end" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17560
|
-
});
|
|
17561
17201
|
|
|
17562
|
-
|
|
17563
|
-
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
17567
|
-
|
|
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
|
+
}
|
|
17568
17233
|
|
|
17569
17234
|
const dateStringToTimestamp = (val) => {
|
|
17570
17235
|
const [dd, mm, yyyy] = val.split('/');
|
|
@@ -17585,6 +17250,440 @@ const dateOrTimestamp = preprocessType(
|
|
|
17585
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' })
|
|
17586
17251
|
);
|
|
17587
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
|
+
|
|
17588
17687
|
const blockscoutParamsSchema = objectType({
|
|
17589
17688
|
address: stringType().nonempty(),
|
|
17590
17689
|
type: enumType(['stat', 'txns', 'tokens']),
|
|
@@ -17595,44 +17694,78 @@ const blockscoutParamsSchema = objectType({
|
|
|
17595
17694
|
offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17596
17695
|
});
|
|
17597
17696
|
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
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);
|
|
17605
17708
|
|
|
17606
|
-
|
|
17607
|
-
|
|
17608
|
-
|
|
17609
|
-
|
|
17610
|
-
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17709
|
+
validateParams(blockscoutParamsSchema, {
|
|
17710
|
+
address,
|
|
17711
|
+
type,
|
|
17712
|
+
chain,
|
|
17713
|
+
startTimestamp,
|
|
17714
|
+
endTimestamp,
|
|
17715
|
+
page,
|
|
17716
|
+
offset
|
|
17717
|
+
});
|
|
17614
17718
|
|
|
17615
|
-
const
|
|
17719
|
+
const startTs =
|
|
17720
|
+
startTimestamp ?? Math.floor((Date.now() - 30 * 24 * 3600 * 1000) / 1000);
|
|
17721
|
+
const endTs = endTimestamp;
|
|
17616
17722
|
|
|
17617
|
-
const
|
|
17618
|
-
type: literalType('gas'),
|
|
17619
|
-
startDate: dateOrTimestamp.optional(),
|
|
17620
|
-
endDate: dateOrTimestamp.optional(),
|
|
17621
|
-
page: numberType().int().nonnegative().default(1),
|
|
17622
|
-
limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17623
|
-
});
|
|
17723
|
+
const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
|
|
17624
17724
|
|
|
17625
|
-
const
|
|
17626
|
-
type: enumType(['all-txns', 'token-txns', 'nft-txns']),
|
|
17627
|
-
address: stringType().nonempty(),
|
|
17628
|
-
startDate: dateOrTimestamp.optional(),
|
|
17629
|
-
endDate: dateOrTimestamp.optional(),
|
|
17630
|
-
chain: enumType(['ethereum','base','gnosis']),
|
|
17631
|
-
page: numberType().int().nonnegative().default(1),
|
|
17632
|
-
limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17633
|
-
});
|
|
17725
|
+
const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
|
|
17634
17726
|
|
|
17635
|
-
|
|
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
|
+
}
|
|
17636
17769
|
|
|
17637
17770
|
const allowedValues = ['1h', '24h', '7d'];
|
|
17638
17771
|
const param2Schema = stringType()
|
|
@@ -17673,123 +17806,67 @@ const coingeckoParamsSchema = discriminatedUnionType('category', [
|
|
|
17673
17806
|
derivativesSchema,
|
|
17674
17807
|
]);
|
|
17675
17808
|
|
|
17676
|
-
const baseSchema = objectType({
|
|
17677
|
-
addresses: stringType().nonempty(),
|
|
17678
|
-
category: enumType(['balance','txns']),
|
|
17679
|
-
chains: preprocessType(
|
|
17680
|
-
(val) => typeof val === 'string'
|
|
17681
|
-
? val.split(',').map(s => s.trim()).filter(Boolean)
|
|
17682
|
-
: val,
|
|
17683
|
-
arrayType(
|
|
17684
|
-
enumType(['ethereum','gnosis','base'])
|
|
17685
|
-
).nonempty()
|
|
17686
|
-
),
|
|
17687
|
-
startTime: dateOrTimestamp.optional(),
|
|
17688
|
-
endTime: dateOrTimestamp.optional(),
|
|
17689
|
-
page: numberType().int().nonnegative().default(1),
|
|
17690
|
-
offset: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"offset" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17691
|
-
});
|
|
17692
|
-
|
|
17693
|
-
const eoaParamsSchema = preprocessType(
|
|
17694
|
-
(val) => {
|
|
17695
|
-
const obj = { ...(val || {}) };
|
|
17696
|
-
// if balance, ignore startTime/endTime
|
|
17697
|
-
if (obj.category === 'balance') {
|
|
17698
|
-
delete obj.startTime;
|
|
17699
|
-
delete obj.endTime;
|
|
17700
|
-
}
|
|
17701
|
-
return obj
|
|
17702
|
-
},
|
|
17703
|
-
baseSchema.refine(data => {
|
|
17704
|
-
// for txns, startTime and endTime are required
|
|
17705
|
-
if (data.category === 'txns') {
|
|
17706
|
-
return data.startTime !== undefined && data.endTime !== undefined
|
|
17707
|
-
}
|
|
17708
|
-
return true
|
|
17709
|
-
}, {
|
|
17710
|
-
message: 'startTime and endTime required for txns',
|
|
17711
|
-
path: ['startTime'],
|
|
17712
|
-
})
|
|
17713
|
-
);
|
|
17714
|
-
|
|
17715
|
-
const safeParamsSchema = objectType({
|
|
17716
|
-
address: stringType().nonempty(),
|
|
17717
|
-
utility: literalType('txns'),
|
|
17718
|
-
chain: enumType(['ethereum','gnosis']),
|
|
17719
|
-
limit: numberType().int().nonnegative().max(MAX_PAGE_LIMIT, {message: `"limit" must be less than or equal to ${MAX_PAGE_LIMIT}`}).default(10),
|
|
17720
|
-
offset: numberType().int().nonnegative().default(0),
|
|
17721
|
-
});
|
|
17722
|
-
|
|
17723
|
-
const categories = ['protocols','yields','dex','fees'];
|
|
17724
|
-
const defillamaParamsSchema = objectType({
|
|
17725
|
-
category: enumType(categories)
|
|
17726
|
-
});
|
|
17727
|
-
|
|
17728
|
-
const CATEGORY_URLS = {
|
|
17729
|
-
protocols: 'https://api.llama.fi/protocols',
|
|
17730
|
-
yields: 'https://yields.llama.fi/pools',
|
|
17731
|
-
dex: 'https://api.llama.fi/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true',
|
|
17732
|
-
fees: 'https://api.llama.fi/overview/fees?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true&dataType=dailyFees'
|
|
17733
|
-
};
|
|
17734
|
-
|
|
17735
|
-
const uniswapParamsSchema = objectType({
|
|
17736
|
-
graphType: enumType(['v3','v3-raw']),
|
|
17737
|
-
category: enumType(['tokens','markets']),
|
|
17738
|
-
param1: stringType().nonempty(),
|
|
17739
|
-
param2: stringType().optional(),
|
|
17740
|
-
});
|
|
17741
|
-
|
|
17742
|
-
const aaveParamsSchema = objectType({
|
|
17743
|
-
graphType: enumType(['v2','v2-raw']),
|
|
17744
|
-
category: enumType(['tokens','markets']),
|
|
17745
|
-
param1: stringType().nonempty(),
|
|
17746
|
-
param2: stringType().optional(),
|
|
17747
|
-
});
|
|
17748
|
-
|
|
17749
17809
|
/* global window */
|
|
17750
17810
|
|
|
17751
17811
|
|
|
17752
17812
|
|
|
17753
|
-
async function
|
|
17813
|
+
async function COINGECKO() {
|
|
17754
17814
|
try {
|
|
17755
|
-
const [
|
|
17756
|
-
|
|
17757
|
-
validateParams(fireflyParamsSchema, {
|
|
17758
|
-
platform,
|
|
17759
|
-
contentType,
|
|
17760
|
-
identifier,
|
|
17761
|
-
start,
|
|
17762
|
-
end,
|
|
17763
|
-
});
|
|
17815
|
+
const [category, param1, param2] = argsToArray(arguments);
|
|
17816
|
+
validateParams(coingeckoParamsSchema, { category, param1, param2 });
|
|
17764
17817
|
|
|
17765
|
-
const apiKey = window.localStorage.getItem(SERVICES_API_KEY.
|
|
17818
|
+
const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
|
|
17766
17819
|
|
|
17767
|
-
const
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
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});
|
|
17779
17852
|
|
|
17780
|
-
const
|
|
17781
|
-
const
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
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)
|
|
17787
17859
|
}
|
|
17788
17860
|
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
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]
|
|
17792
17867
|
}
|
|
17868
|
+
|
|
17869
|
+
const data = Array.isArray(json) ? json : [json];
|
|
17793
17870
|
return data.map(item => {
|
|
17794
17871
|
const flat = {};
|
|
17795
17872
|
for (const [key, value] of Object.entries(item)) {
|
|
@@ -17797,78 +17874,133 @@ async function FIREFLY() {
|
|
|
17797
17874
|
flat[key] = value;
|
|
17798
17875
|
}
|
|
17799
17876
|
}
|
|
17800
|
-
flat.platform = platform;
|
|
17801
17877
|
return flat
|
|
17802
17878
|
})
|
|
17803
|
-
|
|
17804
17879
|
} catch (err) {
|
|
17805
|
-
return errorMessageHandler(err, '
|
|
17880
|
+
return errorMessageHandler(err, 'COINGECKO')
|
|
17806
17881
|
}
|
|
17807
17882
|
}
|
|
17808
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
|
+
};
|
|
17809
17895
|
|
|
17810
|
-
async function
|
|
17896
|
+
async function DEFILLAMA() {
|
|
17811
17897
|
try {
|
|
17812
|
-
const [
|
|
17813
|
-
|
|
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();
|
|
17814
17905
|
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
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
|
+
}
|
|
17821
17918
|
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
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
|
+
}
|
|
17825
17930
|
|
|
17826
|
-
|
|
17827
|
-
|
|
17828
|
-
|
|
17829
|
-
|
|
17830
|
-
|
|
17831
|
-
|
|
17832
|
-
|
|
17833
|
-
.map((s) => s.trim())
|
|
17834
|
-
.filter(Boolean)
|
|
17835
|
-
.join(',')
|
|
17836
|
-
);
|
|
17837
|
-
const typeMap = {
|
|
17838
|
-
posts: 'lensid',
|
|
17839
|
-
replies: 'lenspostid',
|
|
17840
|
-
};
|
|
17841
|
-
url.searchParams.set('type', typeMap[contentType]);
|
|
17842
|
-
url.searchParams.set('start', String(start));
|
|
17843
|
-
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
|
+
});
|
|
17844
17938
|
|
|
17845
|
-
|
|
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
|
+
});
|
|
17846
17948
|
|
|
17847
|
-
|
|
17848
|
-
method: 'GET',
|
|
17849
|
-
headers: HEADERS,
|
|
17850
|
-
});
|
|
17851
|
-
if (!response.ok) {
|
|
17852
|
-
throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
|
|
17853
|
-
}
|
|
17949
|
+
const etherscanParamsSchema = discriminatedUnionType('type', [gasSchema, txnSchema]);
|
|
17854
17950
|
|
|
17855
|
-
|
|
17856
|
-
if (!Array.isArray(data)) return []
|
|
17951
|
+
/* global window */
|
|
17857
17952
|
|
|
17858
|
-
|
|
17859
|
-
|
|
17860
|
-
|
|
17861
|
-
|
|
17862
|
-
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
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,
|
|
17867
17982
|
})
|
|
17868
17983
|
} catch (err) {
|
|
17869
|
-
return errorMessageHandler(err, '
|
|
17984
|
+
return errorMessageHandler(err, 'ETHERSCAN')
|
|
17870
17985
|
}
|
|
17871
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
|
+
|
|
17872
18004
|
async function FARCASTER() {
|
|
17873
18005
|
try {
|
|
17874
18006
|
const [contentType, identifier, start = 0, end = 10] =
|
|
@@ -17935,102 +18067,108 @@ async function FARCASTER() {
|
|
|
17935
18067
|
}
|
|
17936
18068
|
}
|
|
17937
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
|
+
});
|
|
17938
18085
|
|
|
17939
|
-
|
|
17940
|
-
|
|
17941
|
-
|
|
17942
|
-
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
|
|
17946
|
-
|
|
17947
|
-
|
|
17948
|
-
|
|
17949
|
-
|
|
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
|
+
};
|
|
17950
18101
|
|
|
17951
|
-
|
|
17952
|
-
address,
|
|
17953
|
-
type,
|
|
17954
|
-
chain,
|
|
17955
|
-
startTimestamp,
|
|
17956
|
-
endTimestamp,
|
|
17957
|
-
page,
|
|
17958
|
-
offset
|
|
17959
|
-
});
|
|
18102
|
+
/* global window */
|
|
17960
18103
|
|
|
17961
|
-
const startTs =
|
|
17962
|
-
startTimestamp ?? Math.floor((Date.now() - 30 * 24 * 3600 * 1000) / 1000);
|
|
17963
|
-
const endTs = endTimestamp;
|
|
17964
18104
|
|
|
17965
|
-
const resolvedAddress = await fromEnsNameToAddress$1.validateAndGetAddress(address);
|
|
17966
18105
|
|
|
17967
|
-
const hostname = BLOCKSCOUT_CHAINS_MAP[chain];
|
|
17968
18106
|
|
|
17969
|
-
let requestUrl;
|
|
17970
|
-
switch (type) {
|
|
17971
|
-
case 'stat':
|
|
17972
|
-
requestUrl = `${hostname}/api/v2/addresses/${resolvedAddress}/counters`;
|
|
17973
|
-
break
|
|
17974
|
-
case 'txns':
|
|
17975
|
-
requestUrl =
|
|
17976
|
-
`${hostname}/api?module=account&action=txlist` +
|
|
17977
|
-
`&address=${resolvedAddress}` +
|
|
17978
|
-
`&start_timestamp=${startTs}` +
|
|
17979
|
-
`&end_timestamp=${endTs ?? ''}` +
|
|
17980
|
-
`&page=${page}` +
|
|
17981
|
-
`&offset=${offset}` +
|
|
17982
|
-
`&sort=asc`;
|
|
17983
|
-
break
|
|
17984
|
-
case 'tokens':
|
|
17985
|
-
requestUrl =
|
|
17986
|
-
`${hostname}/api?module=account&action=tokenlist` +
|
|
17987
|
-
`&address=${resolvedAddress}`;
|
|
17988
|
-
break
|
|
17989
|
-
}
|
|
17990
18107
|
|
|
17991
|
-
|
|
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
|
+
});
|
|
17992
18141
|
if (!response.ok) {
|
|
17993
|
-
throw new NetworkError(
|
|
18142
|
+
throw new NetworkError(SERVICES_API_KEY.Firefly, response.status)
|
|
17994
18143
|
}
|
|
17995
18144
|
|
|
17996
|
-
const
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
if (json?.result?.includes('Invalid parameter(s)')) {
|
|
18000
|
-
throw new ValidationError('Invalid parameters')
|
|
18001
|
-
}
|
|
18002
|
-
if (json?.result?.includes('Not found')) {
|
|
18003
|
-
throw new ValidationError('Address information not found')
|
|
18145
|
+
const { data } = await response.json();
|
|
18146
|
+
if (!Array.isArray(data)) {
|
|
18147
|
+
return []
|
|
18004
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
|
+
})
|
|
18005
18159
|
|
|
18006
|
-
return type === 'stat' ? [json] : json.result
|
|
18007
18160
|
} catch (err) {
|
|
18008
|
-
return errorMessageHandler(err, '
|
|
18161
|
+
return errorMessageHandler(err, 'FIREFLY')
|
|
18009
18162
|
}
|
|
18010
18163
|
}
|
|
18011
18164
|
|
|
18012
|
-
|
|
18013
|
-
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18165
|
+
/* global window */
|
|
18166
|
+
|
|
18167
|
+
|
|
18168
|
+
|
|
18169
|
+
|
|
18170
|
+
|
|
18017
18171
|
|
|
18018
|
-
return await handleScanRequest({
|
|
18019
|
-
type,
|
|
18020
|
-
address,
|
|
18021
|
-
startDate,
|
|
18022
|
-
endDate,
|
|
18023
|
-
page,
|
|
18024
|
-
offset: limit,
|
|
18025
|
-
apiKey: API_KEY,
|
|
18026
|
-
functionName: 'BASE',
|
|
18027
|
-
chainId: CHAIN_ID_MAP.base,
|
|
18028
|
-
network: 'base'
|
|
18029
|
-
})
|
|
18030
|
-
} catch (error) {
|
|
18031
|
-
return errorMessageHandler(error, 'BASE')
|
|
18032
|
-
}
|
|
18033
|
-
}
|
|
18034
18172
|
async function GNOSIS() {
|
|
18035
18173
|
try {
|
|
18036
18174
|
const [type, address, startDate, endDate, page = 1, limit = 10] =
|
|
@@ -18067,306 +18205,178 @@ async function GNOSIS() {
|
|
|
18067
18205
|
}
|
|
18068
18206
|
}
|
|
18069
18207
|
|
|
18070
|
-
|
|
18071
|
-
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
const [username] = argsToArray(arguments);
|
|
18077
|
-
|
|
18078
|
-
validateParams(neynarParamsSchema, { username });
|
|
18079
|
-
|
|
18080
|
-
const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Neynar);
|
|
18081
|
-
|
|
18082
|
-
const fid = await fromUsernameToFid$1.fromUsernameToFid(username, apiKey);
|
|
18083
|
-
if (!fid) throw new ValidationError(`Invalid username: ${username}`)
|
|
18084
|
-
|
|
18085
|
-
const url = `https://api.neynar.com/v2/farcaster/followers?fid=${fid}`;
|
|
18086
|
-
|
|
18087
|
-
const { URL: finalUrl, HEADERS } = getUrlAndHeaders({
|
|
18088
|
-
url: url.toString(), serviceName: 'Neynar',
|
|
18089
|
-
headers: {
|
|
18090
|
-
'x-api-key': apiKey,
|
|
18091
|
-
'x-neynar-experimental': 'false'
|
|
18092
|
-
}
|
|
18093
|
-
|
|
18094
|
-
});
|
|
18095
|
-
|
|
18096
|
-
const response = await fetch(finalUrl, {
|
|
18097
|
-
method: 'GET',
|
|
18098
|
-
headers: HEADERS,
|
|
18099
|
-
});
|
|
18100
|
-
if (!response.ok) {
|
|
18101
|
-
throw new NetworkError(SERVICES_API_KEY.Neynar, response.status)
|
|
18102
|
-
}
|
|
18103
|
-
|
|
18104
|
-
const json = await response.json();
|
|
18105
|
-
const users = json?.users || [];
|
|
18106
|
-
if (!users.length) return []
|
|
18107
|
-
|
|
18108
|
-
return users.map(({ user }) => ({
|
|
18109
|
-
username: user.username,
|
|
18110
|
-
custody_address: user.custody_address,
|
|
18111
|
-
follower_count: user.follower_count,
|
|
18112
|
-
country: user.profile?.location?.address?.country || '',
|
|
18113
|
-
city: user.profile?.location?.address?.city || '',
|
|
18114
|
-
}))
|
|
18115
|
-
} catch (err) {
|
|
18116
|
-
return errorMessageHandler(err, 'NEYNAR')
|
|
18117
|
-
}
|
|
18118
|
-
}
|
|
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
|
+
});
|
|
18119
18214
|
|
|
18120
|
-
|
|
18121
|
-
// cardId,
|
|
18122
|
-
// startDate,
|
|
18123
|
-
// endDate,
|
|
18124
|
-
// limit = 20,
|
|
18125
|
-
// offset = 0,
|
|
18126
|
-
// }) {
|
|
18127
|
-
// const apiKeyKey = SERVICES_API_KEY.GnosisPay
|
|
18128
|
-
// const API_KEY = window.localStorage.getItem(apiKeyKey);
|
|
18129
|
-
// if (!API_KEY) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.MISSING_KEY}`;
|
|
18130
|
-
// if (!cardId) return `${apiKeyKey}${ERROR_MESSAGES_FLAG.INVALID_PARAM}`;
|
|
18131
|
-
// if(limit > MAX_PAGE_LIMIT){
|
|
18132
|
-
// return ERROR_MESSAGES_FLAG.MAX_PAGE_LIMIT
|
|
18133
|
-
// }
|
|
18215
|
+
/* global window */
|
|
18134
18216
|
|
|
18135
|
-
// const url = new URL(`https://api.gnosispay.com/cards/${cardId}/transactions`);
|
|
18136
|
-
// url.searchParams.set('limit', limit.toString());
|
|
18137
|
-
// url.searchParams.set('offset', offset.toString());
|
|
18138
18217
|
|
|
18139
|
-
// if (!isNaN(toTimestamp(startDate))) {
|
|
18140
|
-
// url.searchParams.set('startDate', new Date(startDate * 1000).toISOString());
|
|
18141
|
-
// }
|
|
18142
18218
|
|
|
18143
|
-
// if (!isNaN(toTimestamp(endDate))) {
|
|
18144
|
-
// url.searchParams.set('endDate', new Date(endDate * 1000).toISOString());
|
|
18145
|
-
// }
|
|
18146
18219
|
|
|
18147
|
-
// try {
|
|
18148
|
-
// const res = await fetch(url.toString(), {
|
|
18149
|
-
// headers: {
|
|
18150
|
-
// Authorization: `Bearer ${API_KEY}`,
|
|
18151
|
-
// 'Content-Type': 'application/json',
|
|
18152
|
-
// },
|
|
18153
|
-
// });
|
|
18154
|
-
|
|
18155
|
-
// if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`);
|
|
18156
|
-
|
|
18157
|
-
// const json = await res.json();
|
|
18158
|
-
|
|
18159
|
-
// if (!Array.isArray(json)) return [];
|
|
18160
|
-
|
|
18161
|
-
// return json.map(tx => ({
|
|
18162
|
-
// createdAt: tx.createdAt,
|
|
18163
|
-
// clearedAt: tx.clearedAt,
|
|
18164
|
-
// country: tx.country,
|
|
18165
|
-
// merchant: tx.merchant,
|
|
18166
|
-
// billingAmount: tx.billingAmount,
|
|
18167
|
-
// billingCurrency: tx.billingCurrency,
|
|
18168
|
-
// transactionAmount: tx.transactionAmount,
|
|
18169
|
-
// transactionCurrency: tx.transactionCurrency,
|
|
18170
|
-
// transactionType: tx.transactionType,
|
|
18171
|
-
// kind: tx.kind,
|
|
18172
|
-
// status: tx.status || null,
|
|
18173
|
-
// mcc: tx.mcc,
|
|
18174
|
-
// }));
|
|
18175
|
-
// } catch (err) {
|
|
18176
|
-
// console.error('GNOSISPAY_CARD_TXNS error:', err);
|
|
18177
|
-
// return ERROR_MESSAGES_FLAG.DEFAULT;
|
|
18178
|
-
// }
|
|
18179
|
-
// }
|
|
18180
18220
|
|
|
18181
|
-
async function
|
|
18221
|
+
async function LENS() {
|
|
18182
18222
|
try {
|
|
18183
|
-
const [
|
|
18223
|
+
const [contentType, identifier, start = 0, end = 10] =
|
|
18184
18224
|
argsToArray(arguments);
|
|
18185
18225
|
|
|
18226
|
+
validateParams(lensParamsSchema, {
|
|
18227
|
+
contentType,
|
|
18228
|
+
identifier,
|
|
18229
|
+
start,
|
|
18230
|
+
end,
|
|
18231
|
+
});
|
|
18186
18232
|
|
|
18187
|
-
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
|
|
18191
|
-
|
|
18192
|
-
const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Etherscan);
|
|
18193
|
-
|
|
18194
|
-
return await handleScanRequest({
|
|
18195
|
-
type,
|
|
18196
|
-
address,
|
|
18197
|
-
startDate,
|
|
18198
|
-
endDate,
|
|
18199
|
-
page,
|
|
18200
|
-
offset: limit,
|
|
18201
|
-
apiKey,
|
|
18202
|
-
functionName: 'ETHERSCAN',
|
|
18203
|
-
chainId,
|
|
18204
|
-
network: chain,
|
|
18205
|
-
})
|
|
18206
|
-
} catch (err) {
|
|
18207
|
-
return errorMessageHandler(err, 'ETHERSCAN')
|
|
18208
|
-
}
|
|
18209
|
-
}
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
async function COINGECKO() {
|
|
18213
|
-
try {
|
|
18214
|
-
const [category, param1, param2] = argsToArray(arguments);
|
|
18215
|
-
validateParams(coingeckoParamsSchema, { category, param1, param2 });
|
|
18216
|
-
|
|
18217
|
-
const apiKey = window.localStorage.getItem(SERVICES_API_KEY.Coingecko);
|
|
18233
|
+
const apiKey = window.localStorage.getItem(
|
|
18234
|
+
SERVICES_API_KEY.Firefly
|
|
18235
|
+
);
|
|
18218
18236
|
|
|
18219
|
-
const
|
|
18220
|
-
|
|
18221
|
-
|
|
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',
|
|
18222
18251
|
};
|
|
18223
|
-
|
|
18224
|
-
|
|
18225
|
-
|
|
18226
|
-
const vs = param2 || 'usd';
|
|
18227
|
-
url = `https://api.coingecko.com/api/v3/simple/price?vs_currencies=${vs}&symbols=${param1}`;
|
|
18228
|
-
break
|
|
18229
|
-
}
|
|
18230
|
-
case 'market': {
|
|
18231
|
-
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' };
|
|
18232
|
-
const _category = map[param1] || '';
|
|
18233
|
-
const trend = param2 ? `&price_change_percentage=${param2}` : '';
|
|
18234
|
-
url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&include_tokens=top&page=1&per_page=100${_category ? `&category=${_category}` : ''}${trend}`;
|
|
18235
|
-
break
|
|
18236
|
-
}
|
|
18237
|
-
case 'stablecoins': {
|
|
18238
|
-
const _category = param1 === 'all' ? 'stablecoins' : param1;
|
|
18239
|
-
const trend = param2 ? `&price_change_percentage=${param2}` : '';
|
|
18240
|
-
url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=${_category}&order=market_cap_desc&page=1&per_page=100${trend}`;
|
|
18241
|
-
break
|
|
18242
|
-
}
|
|
18243
|
-
case 'derivatives': {
|
|
18244
|
-
url = (!param1 || param1 === 'all')
|
|
18245
|
-
? 'https://api.coingecko.com/api/v3/derivatives'
|
|
18246
|
-
: `https://api.coingecko.com/api/v3/derivatives/exchanges/${param1}?include_tickers=all`;
|
|
18247
|
-
break
|
|
18248
|
-
}
|
|
18249
|
-
}
|
|
18250
|
-
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));
|
|
18251
18255
|
|
|
18252
|
-
const
|
|
18253
|
-
|
|
18254
|
-
|
|
18255
|
-
|
|
18256
|
-
|
|
18257
|
-
|
|
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)
|
|
18258
18264
|
}
|
|
18259
18265
|
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
for (const [token, prices] of Object.entries(json))
|
|
18263
|
-
for (const [cur, val] of Object.entries(prices))
|
|
18264
|
-
out[`${token.charAt(0).toUpperCase() + token.slice(1)}_${cur.toUpperCase()}`] = val;
|
|
18265
|
-
return [out]
|
|
18266
|
-
}
|
|
18266
|
+
const { data } = await response.json();
|
|
18267
|
+
if (!Array.isArray(data)) return []
|
|
18267
18268
|
|
|
18268
|
-
|
|
18269
|
-
return data.map(item => {
|
|
18269
|
+
return data.map((item) => {
|
|
18270
18270
|
const flat = {};
|
|
18271
18271
|
for (const [key, value] of Object.entries(item)) {
|
|
18272
|
-
if (typeof value !== 'object'
|
|
18272
|
+
if (value == null || typeof value !== 'object') {
|
|
18273
18273
|
flat[key] = value;
|
|
18274
18274
|
}
|
|
18275
18275
|
}
|
|
18276
|
+
flat.platform = 'lens';
|
|
18276
18277
|
return flat
|
|
18277
18278
|
})
|
|
18278
18279
|
} catch (err) {
|
|
18279
|
-
return errorMessageHandler(err, '
|
|
18280
|
+
return errorMessageHandler(err, 'LENS')
|
|
18280
18281
|
}
|
|
18281
18282
|
}
|
|
18282
18283
|
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18286
|
-
argsToArray(arguments);
|
|
18287
|
-
validateParams(eoaParamsSchema, { addresses, category, chains, startTime, endTime, page, offset });
|
|
18284
|
+
const neynarParamsSchema = objectType({
|
|
18285
|
+
username: stringType().nonempty()
|
|
18286
|
+
});
|
|
18288
18287
|
|
|
18289
|
-
|
|
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
|
+
});
|
|
18290
18297
|
|
|
18291
|
-
|
|
18292
|
-
|
|
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
|
+
};
|
|
18293
18310
|
|
|
18294
|
-
|
|
18295
|
-
for (const inp of INPUTS) {
|
|
18296
|
-
if (isAddress$1.isAddress(inp)) {
|
|
18297
|
-
ADDRESS_MAP[inp.toLowerCase()] = null;
|
|
18298
|
-
} else {
|
|
18299
|
-
const _address = await fromEnsNameToAddress$1.validateAndGetAddress(inp);
|
|
18300
|
-
ADDRESS_MAP[_address.toLowerCase()] = _address;
|
|
18301
|
-
}
|
|
18302
|
-
}
|
|
18303
|
-
const ADDRS = Object.keys(ADDRESS_MAP);
|
|
18304
|
-
const out = [];
|
|
18311
|
+
/* global window */
|
|
18305
18312
|
|
|
18306
|
-
async function fetchJSON(url) {
|
|
18307
|
-
const { URL: finalUrl, HEADERS } = getUrlAndHeaders({ url, serviceName: 'Etherscan', headers: {} });
|
|
18308
|
-
const res = await fetch(finalUrl, {
|
|
18309
|
-
method: 'GET',
|
|
18310
|
-
headers: HEADERS,
|
|
18311
|
-
});
|
|
18312
|
-
if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Etherscan, res.status)
|
|
18313
|
-
const json = await res.json();
|
|
18314
18313
|
|
|
18315
|
-
if (typeof json.result === 'string') {
|
|
18316
|
-
if (json.result.includes('Invalid API Key')) throw new InvalidApiKeyError(SERVICES_API_KEY.Etherscan)
|
|
18317
|
-
if (json.result.includes('Max rate limit reached')) throw new RateLimitError(SERVICES_API_KEY.Etherscan)
|
|
18318
|
-
}
|
|
18319
|
-
return json.result
|
|
18320
|
-
}
|
|
18321
18314
|
|
|
18322
18315
|
|
|
18323
|
-
for (const chain of CHAINS) {
|
|
18324
|
-
const chainId = CHAIN_ID_MAP[chain];
|
|
18325
|
-
if (!chainId) throw new ValidationError(`Invalid chain: ${chain}`)
|
|
18326
18316
|
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
|
|
18330
|
-
|
|
18331
|
-
|
|
18332
|
-
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
|
|
18339
|
-
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18348
|
-
|
|
18349
|
-
`&startblock=${sb}&endblock=${eb}` +
|
|
18350
|
-
`&page=${page}&offset=${offset}&sort=asc&apikey=${apiKey}`;
|
|
18351
|
-
const data = await fetchJSON(url);
|
|
18352
|
-
if (!Array.isArray(data)) return data
|
|
18353
|
-
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'
|
|
18354
18339
|
}
|
|
18355
|
-
|
|
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)
|
|
18356
18349
|
}
|
|
18357
|
-
|
|
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
|
+
}))
|
|
18358
18362
|
} catch (err) {
|
|
18359
|
-
return errorMessageHandler(err, '
|
|
18363
|
+
return errorMessageHandler(err, 'NEYNAR')
|
|
18360
18364
|
}
|
|
18361
18365
|
}
|
|
18362
18366
|
|
|
18363
|
-
|
|
18364
|
-
|
|
18365
|
-
|
|
18366
|
-
|
|
18367
|
-
|
|
18368
|
-
|
|
18369
|
-
}
|
|
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
|
+
|
|
18370
18380
|
|
|
18371
18381
|
async function SAFE() {
|
|
18372
18382
|
try {
|
|
@@ -18403,42 +18413,12 @@ async function SAFE() {
|
|
|
18403
18413
|
}
|
|
18404
18414
|
}
|
|
18405
18415
|
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18411
|
-
|
|
18412
|
-
const url = CATEGORY_URLS[category];
|
|
18413
|
-
if (!url) throw new ValidationError(`Invalid category: ${category}`)
|
|
18414
|
-
const res = await fetch(url);
|
|
18415
|
-
if (!res.ok) throw new NetworkError(SERVICES_API_KEY.Defillama, res.status)
|
|
18416
|
-
let json = await res.json();
|
|
18417
|
-
|
|
18418
|
-
switch (category) {
|
|
18419
|
-
case 'protocols':
|
|
18420
|
-
json = Array.isArray(json) ? json.slice(0, 500) : [];
|
|
18421
|
-
break
|
|
18422
|
-
case 'yields':
|
|
18423
|
-
json = Array.isArray(json.data) ? json.data.slice(0, 500) : [];
|
|
18424
|
-
break
|
|
18425
|
-
case 'dex':
|
|
18426
|
-
case 'fees':
|
|
18427
|
-
json = Array.isArray(json.protocols) ? json.protocols.slice(0, 500) : [];
|
|
18428
|
-
break
|
|
18429
|
-
}
|
|
18430
|
-
|
|
18431
|
-
return (Array.isArray(json) ? json : [json]).map(item => {
|
|
18432
|
-
const out = {};
|
|
18433
|
-
for (const [k, v] of Object.entries(item)) {
|
|
18434
|
-
if (v === null || typeof v !== 'object') out[k] = v;
|
|
18435
|
-
}
|
|
18436
|
-
return out
|
|
18437
|
-
})
|
|
18438
|
-
} catch (err) {
|
|
18439
|
-
return errorMessageHandler(err, 'DEFILLAMA')
|
|
18440
|
-
}
|
|
18441
|
-
}
|
|
18416
|
+
const uniswapParamsSchema = objectType({
|
|
18417
|
+
graphType: enumType(['v3','v3-raw']),
|
|
18418
|
+
category: enumType(['tokens','markets']),
|
|
18419
|
+
param1: stringType().nonempty(),
|
|
18420
|
+
param2: stringType().optional(),
|
|
18421
|
+
});
|
|
18442
18422
|
|
|
18443
18423
|
async function UNISWAP() {
|
|
18444
18424
|
try {
|
|
@@ -18478,43 +18458,12 @@ async function UNISWAP() {
|
|
|
18478
18458
|
}
|
|
18479
18459
|
}
|
|
18480
18460
|
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
validateParams(aaveParamsSchema, { graphType, category, param1, param2 });
|
|
18489
|
-
|
|
18490
|
-
const baseUrl = 'https://onchain-proxy.fileverse.io/third-party';
|
|
18491
|
-
const url =
|
|
18492
|
-
`${baseUrl}` +
|
|
18493
|
-
`?service=aave` +
|
|
18494
|
-
`&graphType=${encodeURIComponent(graphType)}` +
|
|
18495
|
-
`&category=${encodeURIComponent(category)}` +
|
|
18496
|
-
`&input1=${encodeURIComponent(param1)}` +
|
|
18497
|
-
(param2 ? `&input2=${encodeURIComponent(param2)}` : '');
|
|
18498
|
-
|
|
18499
|
-
const res = await fetch(url);
|
|
18500
|
-
if (!res.ok) {
|
|
18501
|
-
throw new NetworkError('AAVE', res.status)
|
|
18502
|
-
}
|
|
18503
|
-
|
|
18504
|
-
const json = await res.json();
|
|
18505
|
-
if (Array.isArray(json)) {
|
|
18506
|
-
return json.map(item => {
|
|
18507
|
-
const flat = {};
|
|
18508
|
-
Object.entries(item).forEach(([k, v]) => {
|
|
18509
|
-
if (v === null || typeof v !== 'object') flat[k] = v;
|
|
18510
|
-
});
|
|
18511
|
-
return flat
|
|
18512
|
-
})
|
|
18513
|
-
}
|
|
18514
|
-
return json
|
|
18515
|
-
} catch (err) {
|
|
18516
|
-
return errorMessageHandler(err, 'AAVE')
|
|
18517
|
-
}
|
|
18461
|
+
async function FLVURL() {
|
|
18462
|
+
return new Promise((resolve) => {
|
|
18463
|
+
setTimeout(() => {
|
|
18464
|
+
resolve([{ Yoo: 'gotcha' }]);
|
|
18465
|
+
}, 10000);
|
|
18466
|
+
})
|
|
18518
18467
|
}
|
|
18519
18468
|
|
|
18520
18469
|
function POLYMARKET() {
|