forked from Cookie-Jar-DAO/cookie-jar-v3
-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathusePendingTokens.ts
More file actions
135 lines (120 loc) · 3.62 KB
/
usePendingTokens.ts
File metadata and controls
135 lines (120 loc) · 3.62 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
"use client";
import { useQueries } from "@tanstack/react-query";
import { erc20Abi, formatUnits, isAddress } from "viem";
import { usePublicClient, useReadContract } from "wagmi";
import { cookieJarAbi } from "@/generated";
/**
* Pending token data structure
*/
export interface PendingToken {
address: string;
name: string;
symbol: string;
balance: bigint;
decimals: number;
formattedBalance: string;
isSwappable: boolean;
estimatedOutput?: bigint;
}
/**
* Hook for reading pending token balances and information using contract data
*/
export const usePendingTokens = (jarAddress: `0x${string}`) => {
const publicClient = usePublicClient();
// Get pending token addresses from contract
const { data: pendingTokenAddresses } = useReadContract({
address: jarAddress,
abi: cookieJarAbi,
functionName: "getPendingTokenAddresses",
});
// For each token, get balance and metadata
const tokenQueries = useQueries({
queries: (pendingTokenAddresses || []).map((token) => ({
queryKey: ["pendingToken", jarAddress, token],
queryFn: async (): Promise<PendingToken | null> => {
if (!publicClient || !isAddress(token)) return null;
try {
const [name, symbol, decimals, balance] = await Promise.all([
publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: "name",
}),
publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: "symbol",
}),
publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: "decimals",
}),
publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: "balanceOf",
args: [jarAddress],
}),
]);
return {
address: token,
name: name as string,
symbol: symbol as string,
balance: balance as bigint,
decimals: decimals as number,
formattedBalance: formatUnits(
balance as bigint,
decimals as number,
),
isSwappable: true, // For now, assume all tokens are swappable
};
} catch (error) {
console.error(`Error fetching token details for ${token}:`, error);
return null;
}
},
enabled: !!pendingTokenAddresses && !!publicClient,
staleTime: 30000, // 30 seconds
})),
});
const pendingTokens = tokenQueries
.map((q) => q.data)
.filter(Boolean) as PendingToken[];
// Helper function to get total estimated value (mock for now)
const getTotalEstimatedValue = (): bigint => {
// In production, this would query DEX oracles for price conversion
return pendingTokens.reduce((total, token) => total + token.balance, 0n);
};
// Helper function to get swappable tokens count
const getSwappableTokensCount = (): number => {
return pendingTokens.filter((token) => token.isSwappable).length;
};
// Helper function to format token balance
const formatTokenBalance = (
balance: bigint,
decimals: number,
maxDecimals: number = 6,
): string => {
const formatted = formatUnits(balance, decimals);
const parts = formatted.split(".");
if (parts.length > 1 && parts[1].length > maxDecimals) {
return `${parts[0]}.${parts[1].substring(0, maxDecimals)}`;
}
return formatted;
};
return {
// Data
pendingTokens,
isLoading: tokenQueries.some((q) => q.isLoading),
error: tokenQueries.find((q) => q.error)?.error,
// Computed values
totalEstimatedValue: getTotalEstimatedValue(),
swappableTokensCount: getSwappableTokensCount(),
// Utilities
formatTokenBalance,
// Query controls
refetch: () => tokenQueries.forEach((q) => q.refetch()),
isRefetching: tokenQueries.some((q) => q.isRefetching),
};
};