Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions pages/api/delete-airdrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}

// Save the token to DeletedToken before deleting to prevent re-claim
if (existingUser.baseVerifyToken) {
await prisma.deletedToken.create({
data: { token: existingUser.baseVerifyToken }
}).catch(() => {
// Token may already be in deleted_tokens (idempotent)
});
}

// Delete the user from the database
try {
await prisma.verifiedUser.delete({
Expand Down
10 changes: 10 additions & 0 deletions pages/api/verify-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
}

// Check if this token was previously used and deleted (sybil bypass prevention)
const previouslyDeleted = await prisma.deletedToken.findUnique({
where: { token: baseVerifyToken }
});
if (previouslyDeleted) {
return res.status(409).json({
error: 'This verification token has been previously used and deleted. Cannot re-claim.'
});
}

const result = await prisma.verifiedUser.upsert({
where: { address: walletAddress },
update: {
Expand Down
4 changes: 2 additions & 2 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ export default function Home({ initialUsers, error }: Props) {
} else {
const errorData = await response.json()

// Handle 400 with traits not satisfied - Twitter account not verified
if (response.status === 400 && errorData.message === 'verification_traits_not_satisfied') {
// Handle 412 with traits not satisfied - Twitter account not verified
if (response.status === 412 && errorData.error === 'X account does not satisfy verification requirements.') {
setVerificationError('Sorry, your X account does not have a blue checkmark. You are not eligible for this airdrop.')
setIsAutoVerification(false) // Reset flag
}
Expand Down
8 changes: 8 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ model CoinbaseVerifiedUser {

@@map("coinbase_verified_users")
}

model DeletedToken {
id String @id @default(cuid())
token String @unique
deletedAt DateTime @default(now()) @map("deleted_at")

@@map("deleted_tokens")
}