Skip to content

Commit

Permalink
chore: cleanup attacker addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
JorikSchellekens committed Feb 28, 2025
1 parent 3ffb56a commit 83a1afe
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 16 deletions.
46 changes: 46 additions & 0 deletions frontend/components/AttackerIdentifier.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client'

import { useStarkProfile } from '@starknet-react/core'
import { BlockieAvatar } from './BlockieAvatar'

interface AttackerIdentifierProps {
address: string
}

export const AttackerIdentifier = ({ address }: AttackerIdentifierProps) => {
// Normalize the address for StarkNet: remove 0x prefix if present, pad to 64 characters, then add 0x prefix
const normalizedAddress = address.startsWith('0x')
? `0x${address.slice(2).padStart(64, '0')}`
: `0x${address.padStart(64, '0')}`

// Cast to the required type for the hook
const starknetAddress = normalizedAddress as `0x${string}`

const { data: profile } = useStarkProfile({ address: starknetAddress })
const formattedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`

// Only use StarkNet profile information if there's a name available
const hasStarknetName = Boolean(profile?.name)

return (
<div className="flex items-center gap-2">
{/* Show StarkNet profile picture or Blockie avatar */}
{hasStarknetName && profile?.profilePicture ? (
<img
src={profile.profilePicture}
alt={profile.name}
className="h-5 w-5 rounded-full"
/>
) : (
<BlockieAvatar address={address} size={20} />
)}
<span>
{hasStarknetName ? (
<span className="font-medium">{profile!.name}</span>
) : (
formattedAddress
)}
</span>
</div>
)
}
50 changes: 34 additions & 16 deletions frontend/components/AttackersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AttackerDetails } from '@/hooks/useAttackers'
import { formatBalance } from '@/lib/utils'
import { ACTIVE_NETWORK } from '@/constants'
import Link from 'next/link'
import { AttackerIdentifier } from './AttackerIdentifier'

export const AttackersList = ({
attackers,
Expand All @@ -17,6 +18,18 @@ export const AttackersList = ({
searchQuery: string
offset: number
}) => {
// Function to generate Voyager URL for an address
const getVoyagerAddressUrl = (address: string) => {
return `${ACTIVE_NETWORK.explorer}/contract/${address}`
}

// Calculate win ratio (breaks / prompts)
const calculateWinRatio = (breakCount: number, promptCount: number): string => {
if (promptCount === 0) return '0%'
const ratio = (breakCount / promptCount) * 100
return `${ratio.toFixed(1)}%`
}

return (
<AnimatePresence mode="wait">
<motion.div
Expand All @@ -43,9 +56,10 @@ export const AttackersList = ({
<div className="h-full w-[1px] bg-[#6F6F6F]"></div>
<p className="col-span-10 pl-4">Attacker address</p>
</div>
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Accrued rewards</div>
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Prompt count</div>
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Break count</div>
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Accrued rewards</div>
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Prompt count</div>
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Break count</div>
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Win ratio</div>
</div>

{/* Attacker Cards */}
Expand All @@ -58,11 +72,8 @@ export const AttackersList = ({
return `${formattedBalance} ${token.symbol}`
})
.join(', ')

const formattedAddress = `${attacker.address.slice(
0,
6
)}...${attacker.address.slice(-4)}`

const winRatio = calculateWinRatio(attacker.breakCount, attacker.promptCount)

return (
<motion.div
Expand All @@ -73,16 +84,16 @@ export const AttackersList = ({
className="bg-[#2E40494D] backdrop-blur-xl p-3 rounded-lg hover:bg-[#2E40497D] cursor-pointer"
key={attacker.address}
>
{/* Remove pointer-events-none to enable this link in future*/}
<Link
href={`/attackers/${attacker.address}`}
className="block pointer-events-none"
href={getVoyagerAddressUrl(attacker.address)}
target="_blank"
rel="noopener noreferrer"
>
{/* Mobile Layout */}
<div className="md:hidden space-y-2">
<div className="flex items-center gap-2">
<span className="text-gray-400">#{offset + idx + 1}</span>
<span className="font-medium">{formattedAddress}</span>
<AttackerIdentifier address={attacker.address} />
</div>
<div className="grid grid-cols-2 gap-2 text-sm">
<div>
Expand All @@ -97,6 +108,10 @@ export const AttackersList = ({
<p className="text-gray-400 text-xs">Breaks</p>
<p>{attacker.breakCount}</p>
</div>
<div>
<p className="text-gray-400 text-xs">Win ratio</p>
<p>{winRatio}</p>
</div>
</div>
</div>

Expand All @@ -105,11 +120,14 @@ export const AttackersList = ({
<div className="col-span-3 grid grid-cols-12 items-center">
<p className="pr-1 col-span-1">{offset + idx + 1}</p>
<div className="h-full w-[1px] bg-[#6F6F6F]"></div>
<div className="col-span-10 pl-4">{formattedAddress}</div>
<div className="col-span-10 pl-4">
<AttackerIdentifier address={attacker.address} />
</div>
</div>
<div className="col-span-3 ps-4">{accruedBalances}</div>
<div className="col-span-3 ps-4">{attacker.promptCount}</div>
<div className="col-span-3 ps-4">{attacker.breakCount}</div>
<div className="col-span-2 ps-4">{accruedBalances}</div>
<div className="col-span-2 ps-4">{attacker.promptCount}</div>
<div className="col-span-2 ps-4">{attacker.breakCount}</div>
<div className="col-span-3 ps-4">{winRatio}</div>
</div>
</Link>
</motion.div>
Expand Down
35 changes: 35 additions & 0 deletions frontend/components/BlockieAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client'

import Blockies from 'react-blockies'

interface BlockieAvatarProps {
address: string
size?: number
}

export const BlockieAvatar = ({ address, size = 24 }: BlockieAvatarProps) => {
// Normalize the address: remove 0x prefix if present, then pad to 64 characters
const normalizedAddress = address.startsWith('0x')
? address.slice(2).padStart(64, '0')
: address.padStart(64, '0')

// Add 0x prefix back for the seed
const seed = `0x${normalizedAddress}`.toLowerCase()

return (
<div
className="overflow-hidden rounded-full"
style={{
height: `${size}px`,
width: `${size}px`,
minWidth: `${size}px`
}}
>
<Blockies
seed={seed}
size={8} // Number of blocks
scale={size / 8} // Size of each block
/>
</div>
)
}
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"next": "15.1.3",
"nextjs-toploader": "^3.7.15",
"react": "18.2.0",
"react-blockies": "^1.4.1",
"react-confetti": "^6.2.3",
"react-dom": "18.2.0",
"react-hook-form": "^7.54.2",
Expand All @@ -42,6 +43,7 @@
"@eslint/eslintrc": "^3",
"@types/node": "^20",
"@types/react": "18.2.0",
"@types/react-blockies": "^1.4.4",
"@types/react-dom": "18.2.0",
"eslint": "^9",
"eslint-config-next": "15.1.3",
Expand Down
23 changes: 23 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 83a1afe

Please sign in to comment.