Skip to content

Commit

Permalink
Merge pull request #41 from daqhris/fix-attestation-page
Browse files Browse the repository at this point in the history
Updated EventAttendanceVerification and fetchPoaps API, add event_ids.json.
  • Loading branch information
daqhris authored Aug 11, 2024
2 parents 38f7a57 + 7f8df8d commit 9f8b70a
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 110 deletions.
153 changes: 74 additions & 79 deletions components/EventAttendanceVerification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,23 @@ import { useEnsAddress } from "wagmi";
import axios from "axios";

interface POAPEvent {
event: {
id: number;
name: string;
description: string;
start_date: string;
end_date: string;
city: string;
};
tokenId: string;
owner: string;
chain: string;
created: string;
imageUrl: string;
contract_address: string;
token_id: string;
name: string;
description: string;
image_url: string;
created_at: string;
event_url: string;
event_id: string;
}

// API_BASE_URL is no longer used, so we can remove this line entirely

const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified }) => {
const [inputAddress, setInputAddress] = useState("");
const [isVerifying, setIsVerifying] = useState(false);
const [proofResult, setProofResult] = useState<string | null>(null);
const [poaps, setPOAPs] = useState<POAPEvent[]>([]);
const [missingPoaps, setMissingPoaps] = useState<string[]>([]);
const [imageLoadErrors, setImageLoadErrors] = useState<Record<string, boolean>>({});

const { data: ensAddress } = useEnsAddress({
Expand All @@ -36,8 +31,8 @@ const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified
setIsVerifying(true);
setProofResult(null);
setPOAPs([]);
setMissingPoaps([]);

// Validate Ethereum address format
const isValidAddress = /^(0x[a-fA-F0-9]{40}|.+\.eth)$/.test(address);
if (!isValidAddress) {
setProofResult("Invalid input. Please enter a valid Ethereum address or ENS name.");
Expand All @@ -47,17 +42,26 @@ const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified

try {
const response = await axios.get(`/api/fetchPoaps?address=${encodeURIComponent(address)}`);

const { poaps, message } = response.data;
const { poaps, missingEventIds, message } = response.data;

if (Array.isArray(poaps) && poaps.length > 0) {
setPOAPs(poaps);
setProofResult(message);
setMissingPoaps(missingEventIds);
if (missingEventIds.length === 0) {
setProofResult(`Proof successful! ${address} has all required POAPs for ETHGlobal Brussels 2024.`);
onVerified();
} else {
setProofResult(`${address} is missing ${missingEventIds.length} required POAPs for ETHGlobal Brussels 2024.`);
}
} else {
setPOAPs([]);
setMissingPoaps([]);
setProofResult(message || "No eligible POAPs found for ETHGlobal Brussels 2024.");
}
} catch (error) {
console.error("Error fetching POAP data:", error);
setPOAPs([]);
setMissingPoaps([]);
if (axios.isAxiosError(error)) {
if (error.response?.status === 400) {
setProofResult(error.response.data.error || "Invalid input. Please check your address and try again.");
Expand All @@ -74,23 +78,14 @@ const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified
} finally {
setIsVerifying(false);
}
}, []);
}, [onVerified]);

useEffect(() => {
if (ensAddress || inputAddress) {
fetchPOAPs(ensAddress || inputAddress);
}
}, [ensAddress, inputAddress, fetchPOAPs]);

const handleVerify = useCallback(() => {
if (poaps.length > 0) {
setProofResult(`Proof successful! ${inputAddress} has attended an ETHGlobal event in Brussels.`);
onVerified();
} else {
setProofResult(`Proof failed. No eligible POAPs found for ${inputAddress} at ETHGlobal events in Brussels.`);
}
}, [poaps, inputAddress, onVerified, setProofResult]);

const handleImageError = (tokenId: string) => {
setImageLoadErrors(prev => ({ ...prev, [tokenId]: true }));
};
Expand All @@ -99,7 +94,7 @@ const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified
<div className="p-4 bg-white shadow rounded-lg">
<h2 className="text-2xl font-bold mb-4">Event Attendance Proof</h2>
<p className="mb-4">
Enter your Ethereum address or ENS name to provide proof of your attendance at an ETHGlobal event in Brussels:
Enter your Ethereum address or ENS name to provide proof of your attendance at ETHGlobal Brussels 2024:
</p>
<input
type="text"
Expand All @@ -108,58 +103,58 @@ const EventAttendanceProof: React.FC<{ onVerified: () => void }> = ({ onVerified
placeholder="Enter Ethereum address or ENS name"
className="w-full p-2 mb-4 border rounded"
/>
<div className="flex space-x-4 mb-4">
<button
onClick={() => fetchPOAPs(ensAddress || inputAddress)}
disabled={!inputAddress || isVerifying}
className="bg-blue-500 text-white p-2 rounded hover:bg-blue-600 disabled:bg-gray-300 flex-1"
>
Fetch POAPs
</button>
<button
onClick={handleVerify}
disabled={isVerifying || poaps.length === 0}
className="bg-green-500 text-white p-2 rounded hover:bg-green-600 disabled:bg-gray-300 flex-1"
>
Confirm Attendance
</button>
</div>
{isVerifying ? (
<p>Fetching POAPs for {ensAddress || inputAddress}...</p>
) : (
<>
{poaps.length > 0 && (
<div className="mt-4">
<h3 className="text-lg font-semibold">Eligible POAPs:</h3>
<ul className="list-none pl-0">
{poaps.map(poap => (
<li key={poap.tokenId} className="flex items-center mb-2">
<div className="w-12 h-12 mr-2 rounded overflow-hidden">
<Image
src={imageLoadErrors[poap.tokenId] ? "/placeholder-poap.png" : poap.imageUrl}
alt={poap.event.name}
width={48}
height={48}
className="object-cover"
onError={() => handleImageError(poap.tokenId)}
unoptimized
/>
</div>
<div>
<p className="font-semibold">{poap.event.name}</p>
<p className="text-sm text-gray-600">{new Date(poap.event.start_date).toLocaleDateString()}</p>
</div>
</li>
))}
</ul>
</div>
)}
</>
<button
onClick={() => fetchPOAPs(ensAddress || inputAddress)}
disabled={!inputAddress || isVerifying}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600 disabled:bg-gray-300 mb-4"
>
{isVerifying ? "Verifying..." : "Verify Attendance"}
</button>
{isVerifying && (
<p className="text-blue-500 mb-4">Verifying attendance for {ensAddress || inputAddress}...</p>
)}
{poaps.length > 0 && (
<div className="mt-4 bg-green-100 p-4 rounded">
<h3 className="text-lg font-semibold text-green-800 mb-2">POAPs Found</h3>
<p className="text-green-700 mb-4">
{missingPoaps.length === 0
? "You have all required POAPs for ETHGlobal Brussels 2024."
: `You have ${poaps.length} out of ${poaps.length + missingPoaps.length} required POAPs.`}
</p>
<div className="flex flex-wrap">
{poaps.map((poap) => (
<div key={poap.token_id} className="mr-4 mb-4">
<Image
src={imageLoadErrors[poap.token_id] ? "/placeholder-poap.png" : poap.image_url}
alt={poap.name}
width={64}
height={64}
className="rounded-full"
onError={() => handleImageError(poap.token_id)}
/>
<p className="text-sm text-center mt-1">{poap.name}</p>
</div>
))}
</div>
</div>
)}
{missingPoaps.length > 0 && (
<div className="mt-4 bg-yellow-100 p-4 rounded">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">Missing POAPs</h3>
<p className="text-yellow-700 mb-2">You are missing the following POAPs:</p>
<ul className="list-disc list-inside">
{missingPoaps.map((eventId) => (
<li key={eventId}>Event ID: {eventId}</li>
))}
</ul>
</div>
)}
{proofResult && (
<p className={`mt-4 ${proofResult.includes("successful") ? "text-green-500" : "text-red-500"}`}>
{proofResult}
</p>
<div className={`mt-4 p-4 rounded ${proofResult.includes("successful") ? "bg-green-100" : "bg-red-100"}`}>
<p className={proofResult.includes("successful") ? "text-green-700" : "text-red-700"}>
{proofResult}
</p>
</div>
)}
</div>
);
Expand Down
10 changes: 10 additions & 0 deletions event_ids.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"eventIds": [
"176334",
"176328",
"176329",
"176330",
"176331",
"176332"
]
}
93 changes: 62 additions & 31 deletions pages/api/fetchPoaps.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,95 @@
import axios from 'axios';
import { readFileSync } from 'fs';
import path from 'path';

const eventIdsPath = path.join(process.cwd(), 'event_ids.json');
const { eventIds } = JSON.parse(readFileSync(eventIdsPath, 'utf8'));

export default async function handler(req, res) {
console.log('Incoming request:', {
method: req.method,
url: req.url,
headers: req.headers,
query: req.query,
body: req.body
});

const { address } = req.query;
const eventId = '16947'; // ETHGlobal Brussels 2024 event ID

if (!address) {
console.error('Address is missing in the request');
return res.status(400).json({ error: 'Address is required' });
}

try {
console.log(`Fetching POAP event data for event ID: ${eventId}`);
const eventResponse = await axios.get(`https://api.poap.tech/events/id/${eventId}`, {
headers: {
'X-API-Key': process.env.POAP_API_KEY
}
});

console.log('POAP event API response:', eventResponse.data);

if (eventResponse.status !== 200) {
throw new Error(`Failed to fetch POAP event: ${eventResponse.statusText}`);
}

const eventData = eventResponse.data;

console.log(`Verifying POAP ownership for address: ${address}`);
const ownershipResponse = await axios.get(`https://api.poap.tech/actions/scan/${address}`, {
console.log(`Fetching POAPs for address: ${address}`);
const poapResponse = await axios.get(`https://api.poap.tech/actions/scan/${address}`, {
headers: {
'X-API-Key': process.env.POAP_API_KEY
}
});

console.log('POAP ownership API response:', ownershipResponse.data);
console.log('POAP API response:', JSON.stringify(poapResponse.data, null, 2));

if (ownershipResponse.status !== 200) {
throw new Error(`Failed to verify POAP ownership: ${ownershipResponse.statusText}`);
if (poapResponse.status !== 200) {
throw new Error(`Failed to fetch POAPs: ${poapResponse.statusText}`);
}

const ownedPOAPs = ownershipResponse.data;
console.log('Owned POAPs:', ownedPOAPs);
const allPoaps = poapResponse.data;
console.log('Fetched POAPs:', JSON.stringify(allPoaps, null, 2));

const hasPOAP = ownedPOAPs.some(poap => poap.event.id === parseInt(eventId));
const requiredPoaps = allPoaps.filter(poap => eventIds.includes(poap.event.id.toString()));
const missingEventIds = eventIds.filter(id => !requiredPoaps.some(poap => poap.event.id.toString() === id));

if (hasPOAP) {
console.log(`POAP found for address ${address}`);
res.status(200).json({ poaps: [eventData], message: `Proof successful! ${address} has attended ETHGlobal Brussels 2024.` });
if (requiredPoaps.length > 0) {
console.log(`Required POAPs found for address ${address}:`, JSON.stringify(requiredPoaps, null, 2));
res.status(200).json({
poaps: requiredPoaps,
missingEventIds: missingEventIds,
message: `Found ${requiredPoaps.length} out of ${eventIds.length} required POAPs.`
});
} else {
console.log(`No POAP found for address ${address}`);
res.status(200).json({ poaps: [], message: "No eligible POAP found for ETHGlobal Brussels 2024." });
console.log(`No required POAPs found for address ${address}`);
res.status(200).json({
poaps: [],
missingEventIds: eventIds,
message: "No required POAPs found for this address."
});
}
} catch (error) {
console.error("Error fetching POAP data:", error);
console.error("Error details:", {
console.error("Full error details:", {
message: error.message,
stack: error.stack,
request: error.config ? {
url: error.config.url,
method: error.config.method,
headers: error.config.headers,
params: error.config.params
} : 'No request config',
response: error.response ? {
status: error.response.status,
statusText: error.response.statusText,
headers: error.response.headers,
data: error.response.data
} : 'No response data'
});
res.status(500).json({ error: 'Failed to fetch POAP data. Please try again.', details: error.message });

let errorMessage = 'Failed to fetch POAP data. Please try again.';
let statusCode = 500;

if (error.response) {
if (error.response.status === 404) {
errorMessage = 'No POAPs found for this address.';
statusCode = 404;
} else if (error.response.status === 401) {
errorMessage = 'Unauthorized access to POAP data. Please check API credentials.';
statusCode = 401;
}
} else if (error.request) {
errorMessage = 'No response received from POAP API. Please try again later.';
}

res.status(statusCode).json({ error: errorMessage, details: error.message });
}
}

0 comments on commit 9f8b70a

Please sign in to comment.