Skip to content

Commit 3512dec

Browse files
committed
chore: update dependencies and refactor wallet connection logic; remove unused components and improve session management for SIWE authentication
1 parent c720e07 commit 3512dec

File tree

25 files changed

+1276
-1243
lines changed

25 files changed

+1276
-1243
lines changed

apps/hub/package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,33 @@
1616
"dependencies": {
1717
"@hookform/devtools": "^4.3.1",
1818
"@hookform/resolvers": "^3.1.1",
19+
"@radix-ui/react-dialog": "^1.1.1",
1920
"@status-im/colors": "workspace:*",
2021
"@status-im/components": "workspace:*",
2122
"@status-im/icons": "workspace:*",
2223
"@status-im/status-network": "workspace:*",
23-
"@tanstack/react-table": "^8.21.3",
2424
"@status-im/wallet": "workspace:*",
2525
"@tanstack/react-query": "5.66.0",
26+
"@tanstack/react-table": "^8.21.3",
2627
"@vercel/analytics": "^1.5.0",
2728
"connectkit": "^1.9.0",
29+
"connectkit-next-siwe": "^0.3.0",
30+
"cookie": "^1.0.2",
2831
"cva": "1.0.0-beta.1",
2932
"framer-motion": "^12.0.6",
33+
"iron-session": "^8.0.4",
3034
"next": "15.3.0",
3135
"next-mdx-remote": "^5.0.0",
32-
"react-hook-form": "^7.45.1",
3336
"pino-pretty": "^13.1.1",
3437
"react": "19.1.0",
3538
"react-dom": "19.1.0",
39+
"react-hook-form": "^7.45.1",
3640
"rehype-slug": "^6.0.0",
3741
"siwe": "^2.3.2",
3842
"ts-pattern": "^5.6.2",
3943
"viem": "^2.21.1",
40-
"zod": "^3.21.4",
41-
"wagmi": "2.15.2"
44+
"wagmi": "2.15.2",
45+
"zod": "^3.21.4"
4246
},
4347
"devDependencies": {
4448
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",

apps/hub/src/app/_components/connect-button.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,36 @@
22

33
import { Button, ShortenAddress } from '@status-im/status-network/components'
44
import { ConnectKitButton } from 'connectkit'
5-
import { useAccount } from 'wagmi'
65

76
import type { ComponentProps } from 'react'
87

98
type Props = {
109
size?: ComponentProps<typeof Button>['size']
1110
label?: string
11+
className?: string
12+
/** If true, shows the label instead of the shortened address when connected */
13+
alwaysShowLabel?: boolean
1214
}
1315

1416
const ConnectButton = (props: Props) => {
15-
const { size = '32', label = 'Connect wallet' } = props
16-
17-
const { address, isConnected } = useAccount()
17+
const {
18+
size = '32',
19+
label = 'Connect wallet',
20+
className,
21+
alwaysShowLabel = false,
22+
} = props
1823

1924
return (
2025
<ConnectKitButton.Custom>
21-
{({ show }) => {
26+
{({ show, isConnected, address }) => {
2227
return (
2328
<Button
2429
onClick={show}
2530
variant={isConnected ? 'secondary' : 'primary'}
2631
size={size}
32+
className={className}
2733
>
28-
{address && isConnected ? (
34+
{address && isConnected && !alwaysShowLabel ? (
2935
<ShortenAddress address={address} />
3036
) : (
3137
label

apps/hub/src/app/_components/vaults/table-columns.tsx

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ import { SNT_TOKEN } from '~constants/index'
88
import { type StakingVault } from '~hooks/useStakingVaults'
99
import { shortenAddress } from '~utils/address'
1010
import { formatSNT } from '~utils/currency'
11-
import {
12-
calculateDaysUntilUnlock,
13-
calculateVaultBoost,
14-
isVaultLocked,
15-
} from '~utils/vault'
11+
import { calculateDaysUntilUnlock, isVaultLocked } from '~utils/vault'
1612

1713
import { LockVaultModal } from './modals/lock-vault-modal'
1814
import { WithdrawVaultModal } from './modals/withdraw-vault-modal'
@@ -25,21 +21,56 @@ interface TableColumnsProps {
2521
isConnected: boolean
2622
}
2723

24+
// Calculate total staked across all vaults
25+
const calculateTotalStaked = (vaults: StakingVault[]): bigint => {
26+
return vaults.reduce(
27+
(acc, vault) => acc + (vault.data?.stakedBalance || 0n),
28+
BigInt(0)
29+
)
30+
}
31+
32+
// Calculate total karma across all vaults
33+
const calculateTotalKarma = (vaults: StakingVault[]): bigint => {
34+
return vaults.reduce(
35+
(acc, vault) => acc + (vault.data?.rewardsAccrued || 0n),
36+
BigInt(0)
37+
)
38+
}
39+
40+
// Cache current time to avoid calling Date.now() on every cell render
41+
const getCurrentTimestamp = (): bigint => {
42+
return BigInt(Math.floor(Date.now() / 1000))
43+
}
44+
45+
// Validation function for lock time - extracted to avoid recreating on every render
46+
const validateLockTime = (_: string, days: string): string | null => {
47+
const totalDays = parseInt(days || '0')
48+
// TODO: read this from the contract
49+
return totalDays > 1460 ? 'Maximum lock time is 4 years' : null
50+
}
51+
52+
// Modal action configurations - static, never change
53+
const EXTEND_LOCK_ACTIONS = [
54+
{ label: 'Cancel' },
55+
{ label: 'Extend lock' },
56+
] as const
57+
58+
const LOCK_VAULT_ACTIONS = [{ label: "Don't lock" }, { label: 'Lock' }] as const
59+
60+
const LOCK_INFO_MESSAGE =
61+
'Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks.' as const
62+
2863
export const createVaultTableColumns = ({
2964
vaults = [],
3065
openModalVaultId,
3166
setOpenModalVaultId,
3267
emergencyModeEnabled,
3368
isConnected,
3469
}: TableColumnsProps) => {
35-
const totalStaked = vaults.reduce(
36-
(acc, vault) => acc + (vault.data?.stakedBalance || 0n),
37-
BigInt(0)
38-
)
39-
const totalKarma = vaults.reduce(
40-
(acc, vault) => acc + (vault.data?.rewardsAccrued || 0n),
41-
BigInt(0)
42-
)
70+
// Calculate totals and current time once per column creation
71+
const totalStaked = calculateTotalStaked(vaults)
72+
const totalKarma = calculateTotalKarma(vaults)
73+
const currentTimestamp = getCurrentTimestamp()
4374
const columnHelper = createColumnHelper<StakingVault>()
4475

4576
return [
@@ -140,10 +171,19 @@ export const createVaultTableColumns = ({
140171
? (maxMP - mpAccrued) / stakedBalance
141172
: undefined
142173

174+
// Calculate boost directly instead of recalculating for all vaults
175+
// Boost = (mpAccrued / stakedBalance) + 1 (base multiplier)
176+
const currentBoost =
177+
stakedBalance > 0n
178+
? Number(formatUnits(mpAccrued, SNT_TOKEN.decimals)) /
179+
Number(formatUnits(stakedBalance, SNT_TOKEN.decimals)) +
180+
1
181+
: 1
182+
143183
return (
144184
<div className="flex items-center gap-3">
145185
<span className="text-[13px] font-medium leading-[1.4] tracking-[-0.039px] text-neutral-100">
146-
x{calculateVaultBoost(vaults, row.original.address)}
186+
x{currentBoost.toFixed(2)}
147187
</span>
148188
{potentialBoost && (
149189
<span className="text-[13px] font-medium leading-[1.4] tracking-[-0.039px] text-[#7140fd]">
@@ -218,8 +258,9 @@ export const createVaultTableColumns = ({
218258
const isWithdrawModalOpen = openModalVaultId === withdrawModalId
219259
const isLockModalOpen = openModalVaultId === lockModalId
220260

261+
// Use cached timestamp instead of calling Date.now() on every render
221262
const isLocked = row.original?.data?.lockUntil
222-
? row.original.data.lockUntil > BigInt(Math.floor(Date.now() / 1000))
263+
? row.original.data.lockUntil > currentTimestamp
223264
: false
224265

225266
return (
@@ -261,16 +302,9 @@ export const createVaultTableColumns = ({
261302
initialYears="2"
262303
initialDays="732"
263304
description="Extending lock time increasing Karma boost"
264-
actions={[
265-
{
266-
label: 'Cancel',
267-
},
268-
{
269-
label: 'Extend lock',
270-
},
271-
]}
305+
actions={EXTEND_LOCK_ACTIONS}
272306
onClose={() => setOpenModalVaultId(null)}
273-
infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks."
307+
infoMessage={LOCK_INFO_MESSAGE}
274308
>
275309
<Button
276310
variant="primary"
@@ -295,23 +329,10 @@ export const createVaultTableColumns = ({
295329
vaultAddress={row.original.address}
296330
title="Do you want to lock the vault?"
297331
description="Lock this vault to receive more Karma"
298-
actions={[
299-
{
300-
label: "Don't lock",
301-
},
302-
{
303-
label: 'Lock',
304-
},
305-
]}
332+
actions={LOCK_VAULT_ACTIONS}
306333
onClose={() => setOpenModalVaultId(null)}
307-
infoMessage="Boost the rate at which you receive Karma. The longer you lock your vault, the higher your boost, and the faster you accumulate Karma. You can add more SNT at any time, but withdrawing your SNT is only possible once the vault unlocks."
308-
onValidate={(_, days) => {
309-
const totalDays = parseInt(days || '0')
310-
// TODO: read this from the contract
311-
return totalDays > 1460
312-
? 'Maximum lock time is 4 years'
313-
: null
314-
}}
334+
infoMessage={LOCK_INFO_MESSAGE}
335+
onValidate={validateLockTime}
315336
>
316337
<Button
317338
variant="primary"

apps/hub/src/app/_components/vaults/vaults-table.tsx

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useMemo, useState } from 'react'
3+
import { useCallback, useMemo, useState } from 'react'
44

55
import { AddCircleIcon } from '@status-im/icons/12'
66
import { Button } from '@status-im/status-network/components'
@@ -48,6 +48,7 @@ interface TableProps {
4848
table: ReturnType<typeof useReactTable<StakingVault>>
4949
}
5050

51+
// Simple table components - TanStack Table handles optimization via getCoreRowModel
5152
function TableHeader({ table }: TableProps) {
5253
return (
5354
<thead className="h-[40px] border-b border-solid border-neutral-10 bg-neutral-5">
@@ -129,20 +130,37 @@ export function VaultsTable() {
129130
address: STAKING_MANAGER.address,
130131
abi: STAKING_MANAGER.abi,
131132
functionName: 'emergencyModeEnabled',
133+
query: {
134+
staleTime: 60_000, // Consider data fresh for 1 minute
135+
},
132136
})
133137

138+
// Stable callback reference prevents column recreation on every render
139+
const handleSetOpenModalVaultId = useCallback(
140+
(vaultId: string | null) => setOpenModalVaultId(vaultId),
141+
[]
142+
)
143+
144+
// Memoize columns to prevent recreation unless dependencies change
134145
const columns = useMemo(
135146
() =>
136147
createVaultTableColumns({
137148
vaults: vaultDataList,
138149
openModalVaultId,
139-
setOpenModalVaultId,
150+
setOpenModalVaultId: handleSetOpenModalVaultId,
140151
emergencyModeEnabled,
141152
isConnected,
142153
}),
143-
[vaultDataList, openModalVaultId, emergencyModeEnabled, isConnected]
154+
[
155+
vaultDataList,
156+
openModalVaultId,
157+
handleSetOpenModalVaultId,
158+
emergencyModeEnabled,
159+
isConnected,
160+
]
144161
)
145162

163+
// Initialize TanStack Table
146164
const table = useReactTable({
147165
data: vaultDataList ?? [],
148166
columns,
@@ -169,15 +187,34 @@ export function VaultsTable() {
169187
</div>
170188

171189
<div className="relative w-full overflow-hidden rounded-16 border border-solid border-neutral-10 bg-white-100">
172-
<div className="max-h-[600px] overflow-auto">
173-
<div className="min-w-[800px]">
174-
<table className="w-full border-collapse">
175-
<TableHeader table={table} />
176-
<TableBody table={table} />
177-
<TableFooter table={table} />
178-
</table>
190+
{!isConnected ? (
191+
<div className="flex items-center justify-center p-12 text-center">
192+
<div>
193+
<p className="text-neutral-50">
194+
Connect your wallet to view your vaults
195+
</p>
196+
</div>
197+
</div>
198+
) : vaultDataList && vaultDataList.length === 0 ? (
199+
<div className="flex items-center justify-center p-12 text-center">
200+
<div>
201+
<p className="text-neutral-50">No vaults found</p>
202+
<p className="mt-2 text-[13px] text-neutral-40">
203+
Click "Add vault" to create your first vault
204+
</p>
205+
</div>
179206
</div>
180-
</div>
207+
) : (
208+
<div className="max-h-[600px] overflow-auto">
209+
<div className="min-w-[800px]">
210+
<table className="w-full border-collapse">
211+
<TableHeader table={table} />
212+
<TableBody table={table} />
213+
<TableFooter table={table} />
214+
</table>
215+
</div>
216+
</div>
217+
)}
181218
</div>
182219
</div>
183220
)

apps/hub/src/app/_constants/chain.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { getDefaultConfig } from 'connectkit'
2+
import { createConfig, http } from 'wagmi'
3+
14
import type { Chain } from 'wagmi/chains'
25

36
export const statusNetworkTestnet: Chain = {
@@ -23,3 +26,21 @@ export const statusNetworkTestnet: Chain = {
2326
},
2427
},
2528
}
29+
30+
export const getDefaultWagmiConfig = () => {
31+
return getDefaultConfig({
32+
chains: [statusNetworkTestnet],
33+
transports: {
34+
[statusNetworkTestnet.id]: http(),
35+
},
36+
enableFamily: false,
37+
// TODO: move to env
38+
walletConnectProjectId: '7ab664eee6a734b14327cdf4678a3431',
39+
appName: 'Status Hub',
40+
appDescription: 'Status Network DeFi Dashboard',
41+
appUrl: 'https://status.app',
42+
appIcon: 'https://status.app/icon.png',
43+
})
44+
}
45+
46+
export const wagmiConfig = createConfig(getDefaultWagmiConfig())
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
type SessionConfig = {
2+
cookieName: string
3+
cookieOptions: {
4+
httpOnly: boolean
5+
secure: boolean
6+
sameSite: 'lax' | 'strict' | 'none'
7+
maxAge: number
8+
path: string
9+
}
10+
password: string
11+
}
12+
13+
export const sessionConfig: SessionConfig = {
14+
cookieName: 'status-hub-siwe',
15+
cookieOptions: {
16+
httpOnly: true,
17+
secure: false,
18+
sameSite: 'lax',
19+
maxAge: 60 * 60,
20+
path: '/',
21+
},
22+
password:
23+
'complex_password_at_least_32_characters_long_replace_in_production_env',
24+
}

0 commit comments

Comments
 (0)