-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathuseTokenGate.ts
More file actions
154 lines (132 loc) · 4.56 KB
/
Copy pathuseTokenGate.ts
File metadata and controls
154 lines (132 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"use client";
import { useState, useEffect, useCallback } from "react";
import { useWalletContext } from "@/providers/WalletProvider";
import { useTokenBalance } from "./useTokenBalance";
import { SR_MIN_USD_VALUE, TOKEN_GATE_SESSION_DURATION } from "@/lib/config";
import {
getTokenGateSession,
setTokenGateSession,
clearTokenGateSession,
isSessionValid,
getSessionTimeRemaining,
type TokenGateSession,
} from "@/lib/tokenGateSession";
interface TokenGateState {
// Gate status
isGated: boolean;
isVerifying: boolean;
// Balance info (USD-based)
balance: number; // Token balance
usdValue: number; // USD value of holdings
priceUsd: number; // Current token price
minRequiredUsd: number; // Min USD required ($100)
// Session info
lastVerified: number | null;
sessionTimeRemaining: number;
// Actions
verify: () => Promise<boolean>;
clearSession: () => void;
}
/**
* Token gate hook that manages wallet verification and session state
* Now uses USD value ($100 minimum) instead of token count
*
* Flow:
* 1. On mount: Check cookie for existing session
* 2. If session exists and not expired (12h) AND wallet matches: use cached values
* 3. If session expired OR wallet changed: re-verify balance + price on chain
* 4. Auto re-verify silently when session nears expiration
*/
export function useTokenGate(): TokenGateState {
const { connected, publicKey } = useWalletContext();
const {
balance: chainBalance,
usdValue: chainUsdValue,
priceUsd: chainPriceUsd,
isLoading: isLoadingBalance,
refetch: refetchBalance,
} = useTokenBalance(publicKey);
const [session, setSession] = useState<TokenGateSession | null>(null);
const [isVerifying, setIsVerifying] = useState(false);
const [sessionTimeRemaining, setSessionTimeRemaining] = useState(0);
// Load session from cookies on mount
useEffect(() => {
const savedSession = getTokenGateSession();
if (savedSession && isSessionValid(savedSession, publicKey)) {
setSession(savedSession);
setSessionTimeRemaining(getSessionTimeRemaining(savedSession));
}
}, [publicKey]);
// Update session time remaining periodically
useEffect(() => {
if (!session) return;
const interval = setInterval(() => {
const remaining = getSessionTimeRemaining(session);
setSessionTimeRemaining(remaining);
// If session expired, clear it
if (remaining <= 0) {
setSession(null);
clearTokenGateSession();
}
}, 60000); // Check every minute
return () => clearInterval(interval);
}, [session]);
// Verify balance and create/update session
const verify = useCallback(async (): Promise<boolean> => {
if (!connected || !publicKey) {
return false;
}
setIsVerifying(true);
try {
// Refetch balance + price from chain
const { balance, usdValue, priceUsd } = await refetchBalance();
// Create new session with the fresh values
const newSession: TokenGateSession = {
walletAddress: publicKey,
balance,
usdValue,
priceUsd,
verifiedAt: Date.now(),
};
setSession(newSession);
setTokenGateSession(newSession);
setSessionTimeRemaining(TOKEN_GATE_SESSION_DURATION);
// Gate based on USD value, not token count
return usdValue >= SR_MIN_USD_VALUE;
} catch (error) {
console.error("Token gate verification failed:", error);
return false;
} finally {
setIsVerifying(false);
}
}, [connected, publicKey, refetchBalance]);
// Auto-verify when wallet connects but no valid session
useEffect(() => {
if (connected && publicKey && !isSessionValid(session, publicKey) && !isVerifying) {
verify();
}
}, [connected, publicKey, session, verify]);
// Clear session and require re-verification
const clearSessionHandler = useCallback(() => {
setSession(null);
clearTokenGateSession();
setSessionTimeRemaining(0);
}, []);
// Determine if user passes the gate (USD-based)
const effectiveBalance = session?.balance ?? chainBalance;
const effectiveUsdValue = session?.usdValue ?? chainUsdValue;
const effectivePriceUsd = session?.priceUsd ?? chainPriceUsd;
const isGated = connected && effectiveUsdValue >= SR_MIN_USD_VALUE;
return {
isGated,
isVerifying: isVerifying || isLoadingBalance,
balance: effectiveBalance,
usdValue: effectiveUsdValue,
priceUsd: effectivePriceUsd,
minRequiredUsd: SR_MIN_USD_VALUE,
lastVerified: session?.verifiedAt ?? null,
sessionTimeRemaining,
verify,
clearSession: clearSessionHandler,
};
}