Skip to content
This repository was archived by the owner on Mar 17, 2026. It is now read-only.

Commit 763186a

Browse files
committed
update to use etherscan v2 api
1 parent 5e5211a commit 763186a

2 files changed

Lines changed: 42 additions & 12 deletions

File tree

  • apps/web
    • app/(basenames)/api/proxy
    • src/components/Basenames/UsernameProfileSectionHeatmap

apps/web/app/(basenames)/api/proxy/route.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ export async function GET(req: NextRequest) {
1919
try {
2020
switch (apiType) {
2121
case 'etherscan':
22-
apiUrl = `https://api.etherscan.io/api?address=${address}&apikey=${ETHERSCAN_API_KEY}&module=account&action=txlist`;
22+
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlist&address=${address}&chainid=1&apikey=${ETHERSCAN_API_KEY}`;
2323
break;
2424
case 'base-sepolia':
25-
apiUrl = `https://api-sepolia.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlistinternal`;
25+
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlistinternal&address=${address}&chainid=84532&apikey=${ETHERSCAN_API_KEY}`;
2626
break;
2727
case 'basescan':
28-
apiUrl = `https://api.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlist`;
28+
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlist&address=${address}&chainid=8453&apikey=${ETHERSCAN_API_KEY}`;
2929
break;
3030
case 'basescan-internal':
31-
apiUrl = `https://api.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlistinternal`;
31+
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlistinternal&address=${address}&chainid=8453&apikey=${ETHERSCAN_API_KEY}`;
3232
break;
3333
default:
3434
return NextResponse.json({ error: 'Invalid apiType parameter' }, { status: 400 });
@@ -43,13 +43,32 @@ export async function GET(req: NextRequest) {
4343
});
4444

4545
const contentType = externalResponse.headers.get('content-type');
46+
const maskedApiUrl = apiUrl.replace(/(apikey=)[^&]+/i, '$1****');
4647
let responseData;
4748
if (contentType?.includes('application/json')) {
4849
responseData = await externalResponse.json();
4950
} else {
5051
responseData = await externalResponse.text();
5152
}
5253

54+
// Log upstream V1 deprecation warnings safely (masked URL)
55+
try {
56+
const maybeJson = typeof responseData === 'string' ? JSON.parse(responseData) : responseData;
57+
if (
58+
maybeJson?.status === '0' &&
59+
typeof maybeJson?.message === 'string' &&
60+
maybeJson.message.toLowerCase().includes('deprecated')
61+
) {
62+
console.warn('[api/proxy] Upstream API deprecation warning', {
63+
apiType,
64+
apiUrl: maskedApiUrl,
65+
message: maybeJson.message,
66+
});
67+
}
68+
} catch {
69+
// ignore non-JSON bodies
70+
}
71+
5372
if (externalResponse.ok) {
5473
return NextResponse.json({ data: responseData });
5574
} else {

apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/index.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,21 +166,32 @@ export default function UsernameProfileSectionHeatmap() {
166166
async (apiUrl: string, retryCount = 3): Promise<Transaction[]> => {
167167
try {
168168
const response = await fetch(apiUrl);
169-
const json = (await response.json()) as {
170-
data: { result: Transaction[]; status: '1' | '0'; message: string };
171-
};
169+
const json = (await response.json()) as any;
170+
const data = json?.data ?? json;
172171

173-
if (json.data?.status === '1' && Array.isArray(json.data.result)) {
174-
return json.data.result;
175-
} else if (json.data?.status === '0' && json.data.message === 'No transactions found') {
172+
if (data?.status === '1' && Array.isArray(data?.result)) {
173+
return data.result as Transaction[];
174+
}
175+
176+
// Support potential V2 nested shape: data.data.result
177+
if (Array.isArray(data?.data?.result)) {
178+
return data.data.result as Transaction[];
179+
}
180+
181+
// Some endpoints may omit status but still return an array
182+
if (Array.isArray(data?.result)) {
183+
return data.result as Transaction[];
184+
}
185+
186+
if (data?.status === '0' && data?.message === 'No transactions found') {
176187
return []; // Return an empty array for no transactions
177-
} else if (json.data?.status === '0' && json.data.message === 'Exception') {
188+
} else if (data?.status === '0' && data?.message === 'Exception') {
178189
if (retryCount > 0) {
179190
console.log(`API returned an exception. Retrying... (${retryCount} attempts left)`);
180191
await new Promise((resolve) => setTimeout(resolve, 2000));
181192
return await fetchTransactions(apiUrl, retryCount - 1);
182193
} else {
183-
throw new Error(`API Error: ${json.data.message}`);
194+
throw new Error(`API Error: ${data?.message}`);
184195
}
185196
} else {
186197
console.error('Unexpected API response structure:', json);

0 commit comments

Comments
 (0)