Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions apps/web/app/(basenames)/api/proxy/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from 'next/server';
import { isAddress } from 'viem';

const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
const BASESCAN_API_KEY = process.env.BASESCAN_API_KEY;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all uses ETHERSCAN API key now

const TALENT_PROTOCOL_API_KEY = process.env.TALENT_PROTOCOL_API_KEY;

export async function GET(req: NextRequest) {
Expand All @@ -19,16 +18,16 @@ export async function GET(req: NextRequest) {
try {
switch (apiType) {
case 'etherscan':
apiUrl = `https://api.etherscan.io/api?address=${address}&apikey=${ETHERSCAN_API_KEY}&module=account&action=txlist`;
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlist&address=${address}&chainid=1&apikey=${ETHERSCAN_API_KEY}`;
break;
case 'base-sepolia':
apiUrl = `https://api-sepolia.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlistinternal`;
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlistinternal&address=${address}&chainid=84532&apikey=${ETHERSCAN_API_KEY}`;
break;
case 'basescan':
apiUrl = `https://api.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlist`;
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlist&address=${address}&chainid=8453&apikey=${ETHERSCAN_API_KEY}`;
break;
case 'basescan-internal':
apiUrl = `https://api.basescan.org/api?address=${address}&apikey=${BASESCAN_API_KEY}&module=account&action=txlistinternal`;
apiUrl = `https://api.etherscan.io/v2/api?module=account&action=txlistinternal&address=${address}&chainid=8453&apikey=${ETHERSCAN_API_KEY}`;
break;
default:
return NextResponse.json({ error: 'Invalid apiType parameter' }, { status: 400 });
Expand All @@ -49,7 +48,6 @@ export async function GET(req: NextRequest) {
} else {
responseData = await externalResponse.text();
}

if (externalResponse.ok) {
return NextResponse.json({ data: responseData });
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from 'apps/web/src/components/Basenames/UsernameProfileSectionHeatmap/contracts';
import { Icon } from 'apps/web/src/components/Icon/Icon';
import Image from 'next/image';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ComponentType, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import CalendarHeatmap, { ReactCalendarHeatmapValue } from 'react-calendar-heatmap';
import { Address } from 'viem';
import './cal.css';
Expand Down Expand Up @@ -162,25 +162,53 @@ export default function UsernameProfileSectionHeatmap() {
};
};

type EtherscanEnvelope = {
status?: '1' | '0';
message?: string;
result?: unknown;
data?: { result?: unknown };
};

const isObject = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null;
};

const hasDataEnvelope = (value: unknown): value is { data: EtherscanEnvelope } => {
return isObject(value) && 'data' in value && isObject((value as { data?: unknown }).data);
};

const fetchTransactions = useCallback(
async (apiUrl: string, retryCount = 3): Promise<Transaction[]> => {
try {
const response = await fetch(apiUrl);
const json = (await response.json()) as {
data: { result: Transaction[]; status: '1' | '0'; message: string };
};
const json: unknown = await response.json();
const data: EtherscanEnvelope = hasDataEnvelope(json)
? (json as { data: EtherscanEnvelope }).data
: (json as EtherscanEnvelope);

if (data?.status === '1' && Array.isArray(data?.result)) {
return data.result as Transaction[];
}

// Support potential V2 nested shape: data.data.result
if (Array.isArray(data?.data?.result)) {
return data.data.result as Transaction[];
}

// Some endpoints may omit status but still return an array
if (Array.isArray(data?.result)) {
return data.result as Transaction[];
}

if (json.data?.status === '1' && Array.isArray(json.data.result)) {
return json.data.result;
} else if (json.data?.status === '0' && json.data.message === 'No transactions found') {
if (data?.status === '0' && data?.message === 'No transactions found') {
return []; // Return an empty array for no transactions
} else if (json.data?.status === '0' && json.data.message === 'Exception') {
} else if (data?.status === '0' && data?.message === 'Exception') {
if (retryCount > 0) {
console.log(`API returned an exception. Retrying... (${retryCount} attempts left)`);
await new Promise((resolve) => setTimeout(resolve, 2000));
return await fetchTransactions(apiUrl, retryCount - 1);
} else {
throw new Error(`API Error: ${json.data.message}`);
throw new Error(`API Error: ${data?.message}`);
}
} else {
console.error('Unexpected API response structure:', json);
Expand Down Expand Up @@ -444,14 +472,21 @@ export default function UsernameProfileSectionHeatmap() {
style={{ direction: 'rtl' }}
className="w-full max-w-full overflow-x-auto overflow-y-hidden whitespace-nowrap"
>
<CalendarHeatmap
startDate={new Date(new Date().setFullYear(new Date().getFullYear() - 1))}
endDate={new Date()}
horizontal
values={heatmapData}
classForValue={classForValue}
titleForValue={titleForValue}
/>
{(() => {
const AnyCalendarHeatmap = CalendarHeatmap as unknown as ComponentType<
Record<string, unknown>
>;
return (
<AnyCalendarHeatmap
startDate={new Date(new Date().setFullYear(new Date().getFullYear() - 1))}
endDate={new Date()}
horizontal
values={heatmapData}
classForValue={classForValue}
titleForValue={titleForValue}
/>
);
})()}
</div>
</div>
<Collapsible.Trigger className="flex w-full flex-row items-center border-t border-palette-line/20 px-6 py-4">
Expand Down