Skip to content

Commit df3802f

Browse files
chore: cleanup attacker addresses
1 parent 3ffb56a commit df3802f

File tree

4 files changed

+117
-16
lines changed

4 files changed

+117
-16
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use client'
2+
3+
import { useStarkProfile } from '@starknet-react/core'
4+
import { BlockieAvatar } from './BlockieAvatar'
5+
6+
interface AttackerIdentifierProps {
7+
address: string
8+
}
9+
10+
export const AttackerIdentifier = ({ address }: AttackerIdentifierProps) => {
11+
// Normalize the address for StarkNet: remove 0x prefix if present, pad to 64 characters, then add 0x prefix
12+
const normalizedAddress = address.startsWith('0x')
13+
? `0x${address.slice(2).padStart(64, '0')}`
14+
: `0x${address.padStart(64, '0')}`
15+
16+
// Cast to the required type for the hook
17+
const starknetAddress = normalizedAddress as `0x${string}`
18+
19+
const { data: profile } = useStarkProfile({ address: starknetAddress })
20+
const formattedAddress = `${address.slice(0, 6)}...${address.slice(-4)}`
21+
22+
// Only use StarkNet profile information if there's a name available
23+
const hasStarknetName = Boolean(profile?.name)
24+
25+
return (
26+
<div className="flex items-center gap-2">
27+
{/* Show StarkNet profile picture or Blockie avatar */}
28+
{hasStarknetName && profile?.profilePicture ? (
29+
<img
30+
src={profile.profilePicture}
31+
alt={profile.name}
32+
className="h-5 w-5 rounded-full"
33+
/>
34+
) : (
35+
<BlockieAvatar address={address} size={20} />
36+
)}
37+
<span>
38+
{hasStarknetName ? (
39+
<span className="font-medium">{profile!.name}</span>
40+
) : (
41+
formattedAddress
42+
)}
43+
</span>
44+
</div>
45+
)
46+
}

frontend/components/AttackersList.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AttackerDetails } from '@/hooks/useAttackers'
55
import { formatBalance } from '@/lib/utils'
66
import { ACTIVE_NETWORK } from '@/constants'
77
import Link from 'next/link'
8+
import { AttackerIdentifier } from './AttackerIdentifier'
89

910
export const AttackersList = ({
1011
attackers,
@@ -17,6 +18,18 @@ export const AttackersList = ({
1718
searchQuery: string
1819
offset: number
1920
}) => {
21+
// Function to generate Voyager URL for an address
22+
const getVoyagerAddressUrl = (address: string) => {
23+
return `${ACTIVE_NETWORK.explorer}/contract/${address}`
24+
}
25+
26+
// Calculate win ratio (breaks / prompts)
27+
const calculateWinRatio = (breakCount: number, promptCount: number): string => {
28+
if (promptCount === 0) return '0%'
29+
const ratio = (breakCount / promptCount) * 100
30+
return `${ratio.toFixed(1)}%`
31+
}
32+
2033
return (
2134
<AnimatePresence mode="wait">
2235
<motion.div
@@ -43,9 +56,10 @@ export const AttackersList = ({
4356
<div className="h-full w-[1px] bg-[#6F6F6F]"></div>
4457
<p className="col-span-10 pl-4">Attacker address</p>
4558
</div>
46-
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Accrued rewards</div>
47-
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Prompt count</div>
48-
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Break count</div>
59+
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Accrued rewards</div>
60+
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Prompt count</div>
61+
<div className="col-span-2 border-l border-l-[#6F6F6F] ps-4">Break count</div>
62+
<div className="col-span-3 border-l border-l-[#6F6F6F] ps-4">Win ratio</div>
4963
</div>
5064

5165
{/* Attacker Cards */}
@@ -58,11 +72,8 @@ export const AttackersList = ({
5872
return `${formattedBalance} ${token.symbol}`
5973
})
6074
.join(', ')
61-
62-
const formattedAddress = `${attacker.address.slice(
63-
0,
64-
6
65-
)}...${attacker.address.slice(-4)}`
75+
76+
const winRatio = calculateWinRatio(attacker.breakCount, attacker.promptCount)
6677

6778
return (
6879
<motion.div
@@ -73,16 +84,16 @@ export const AttackersList = ({
7384
className="bg-[#2E40494D] backdrop-blur-xl p-3 rounded-lg hover:bg-[#2E40497D] cursor-pointer"
7485
key={attacker.address}
7586
>
76-
{/* Remove pointer-events-none to enable this link in future*/}
7787
<Link
78-
href={`/attackers/${attacker.address}`}
79-
className="block pointer-events-none"
88+
href={getVoyagerAddressUrl(attacker.address)}
89+
target="_blank"
90+
rel="noopener noreferrer"
8091
>
8192
{/* Mobile Layout */}
8293
<div className="md:hidden space-y-2">
8394
<div className="flex items-center gap-2">
8495
<span className="text-gray-400">#{offset + idx + 1}</span>
85-
<span className="font-medium">{formattedAddress}</span>
96+
<AttackerIdentifier address={attacker.address} />
8697
</div>
8798
<div className="grid grid-cols-2 gap-2 text-sm">
8899
<div>
@@ -97,6 +108,10 @@ export const AttackersList = ({
97108
<p className="text-gray-400 text-xs">Breaks</p>
98109
<p>{attacker.breakCount}</p>
99110
</div>
111+
<div>
112+
<p className="text-gray-400 text-xs">Win ratio</p>
113+
<p>{winRatio}</p>
114+
</div>
100115
</div>
101116
</div>
102117

@@ -105,11 +120,14 @@ export const AttackersList = ({
105120
<div className="col-span-3 grid grid-cols-12 items-center">
106121
<p className="pr-1 col-span-1">{offset + idx + 1}</p>
107122
<div className="h-full w-[1px] bg-[#6F6F6F]"></div>
108-
<div className="col-span-10 pl-4">{formattedAddress}</div>
123+
<div className="col-span-10 pl-4">
124+
<AttackerIdentifier address={attacker.address} />
125+
</div>
109126
</div>
110-
<div className="col-span-3 ps-4">{accruedBalances}</div>
111-
<div className="col-span-3 ps-4">{attacker.promptCount}</div>
112-
<div className="col-span-3 ps-4">{attacker.breakCount}</div>
127+
<div className="col-span-2 ps-4">{accruedBalances}</div>
128+
<div className="col-span-2 ps-4">{attacker.promptCount}</div>
129+
<div className="col-span-2 ps-4">{attacker.breakCount}</div>
130+
<div className="col-span-3 ps-4">{winRatio}</div>
113131
</div>
114132
</Link>
115133
</motion.div>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use client'
2+
3+
import Blockies from 'react-blockies'
4+
5+
interface BlockieAvatarProps {
6+
address: string
7+
size?: number
8+
}
9+
10+
export const BlockieAvatar = ({ address, size = 24 }: BlockieAvatarProps) => {
11+
// Normalize the address: remove 0x prefix if present, then pad to 64 characters
12+
const normalizedAddress = address.startsWith('0x')
13+
? address.slice(2).padStart(64, '0')
14+
: address.padStart(64, '0')
15+
16+
// Add 0x prefix back for the seed
17+
const seed = `0x${normalizedAddress}`.toLowerCase()
18+
19+
return (
20+
<div
21+
className="overflow-hidden rounded-full"
22+
style={{
23+
height: `${size}px`,
24+
width: `${size}px`,
25+
minWidth: `${size}px`
26+
}}
27+
>
28+
<Blockies
29+
seed={seed}
30+
size={8} // Number of blocks
31+
scale={size / 8} // Size of each block
32+
/>
33+
</div>
34+
)
35+
}

frontend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"next": "15.1.3",
2626
"nextjs-toploader": "^3.7.15",
2727
"react": "18.2.0",
28+
"react-blockies": "^1.4.1",
2829
"react-confetti": "^6.2.3",
2930
"react-dom": "18.2.0",
3031
"react-hook-form": "^7.54.2",
@@ -42,6 +43,7 @@
4243
"@eslint/eslintrc": "^3",
4344
"@types/node": "^20",
4445
"@types/react": "18.2.0",
46+
"@types/react-blockies": "^1.4.4",
4547
"@types/react-dom": "18.2.0",
4648
"eslint": "^9",
4749
"eslint-config-next": "15.1.3",

0 commit comments

Comments
 (0)