Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/common/overall-layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ export default function RootLayout({
return;
}

// Handle connection errors with retry logic
if (error instanceof Error && (
error.message.includes("Could not establish connection") ||
error.message.includes("Receiving end does not exist") ||
error.message.includes("Cannot read properties of undefined")
)) {
console.log("Browser extension connection error detected, retrying in 2 seconds...");
setTimeout(() => {
window.location.reload();
}, 2000);
return;
}

// For other errors, don't throw to prevent app crash
// The user can retry by reconnecting their wallet
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function WalletDataLoaderWrapper({
const setWalletAssetMetadata = useWalletsStore(
(state) => state.setWalletAssetMetadata,
);
const { fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo, setProxies } = useProxyActions();
const { fetchAllProxyData, setProxies } = useProxyActions();

const setDrepInfo = useWalletsStore((state) => state.setDrepInfo);

Expand Down Expand Up @@ -189,49 +189,20 @@ export default function WalletDataLoaderWrapper({
walletId: appWallet.id,
});


// First, add proxies to the store
setProxies(appWallet.id, proxies);

// Fetch balance and DRep info for each proxy
for (const proxy of proxies) {
try {

// Fetch balance
await fetchProxyBalance(
appWallet.id,
proxy.id,
proxy.proxyAddress,
network.toString()
);

// Fetch DRep info with force refresh
await fetchProxyDrepInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true // Force refresh to bypass cache
);

// Fetch delegators info with force refresh
await fetchProxyDelegatorsInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true // Force refresh to bypass cache
);

} catch (error) {
console.error(`WalletDataLoaderWrapper: Error fetching data for proxy ${proxy.id}:`, error);
}
// Fetch all proxy data in parallel using the new batch function
if (proxies.length > 0) {
console.log(`WalletDataLoaderWrapper: Fetching data for ${proxies.length} proxies in parallel`);
await fetchAllProxyData(
appWallet.id,
proxies,
appWallet.scriptCbor,
network.toString(),
false // Use cache to avoid duplicate requests
);
console.log("WalletDataLoaderWrapper: Successfully fetched all proxy data");
}
} catch (error) {
console.error("WalletDataLoaderWrapper: Error fetching proxy data:", error);
Expand Down Expand Up @@ -267,9 +238,6 @@ export default function WalletDataLoaderWrapper({
if (appWallet && prevWalletIdRef.current !== appWallet.id) {
refreshWallet();
prevWalletIdRef.current = appWallet.id;
} else if (appWallet) {
// If wallet exists but we already have data, still fetch proxy data
fetchProxyData();
}
}, [appWallet]);

Expand Down
58 changes: 12 additions & 46 deletions src/components/common/overall-layout/wallet-data-loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function WalletDataLoader() {
const ctx = api.useUtils();
const network = useSiteStore((state) => state.network);
const setRandomState = useSiteStore((state) => state.setRandomState);
const { fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo, setProxies } = useProxyActions();
const { fetchAllProxyData, setProxies } = useProxyActions();

async function fetchUtxos() {
if (appWallet) {
Expand Down Expand Up @@ -70,47 +70,17 @@ export default function WalletDataLoader() {
// First, add proxies to the store
setProxies(appWallet.id, proxies);

// Fetch balance and DRep info for each proxy
for (const proxy of proxies) {
try {
console.log(`WalletDataLoader: Fetching data for proxy ${proxy.id}`);

// Fetch balance
await fetchProxyBalance(
appWallet.id,
proxy.id,
proxy.proxyAddress,
network.toString()
);

// Fetch DRep info with force refresh
await fetchProxyDrepInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true // Force refresh to bypass cache
);

// Fetch delegators info with force refresh
await fetchProxyDelegatorsInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true // Force refresh to bypass cache
);

console.log(`WalletDataLoader: Successfully fetched data for proxy ${proxy.id}`);
} catch (error) {
console.error(`WalletDataLoader: Error fetching data for proxy ${proxy.id}:`, error);
}
// Fetch all proxy data in parallel using the new batch function
if (proxies.length > 0) {
console.log(`WalletDataLoader: Fetching data for ${proxies.length} proxies in parallel`);
await fetchAllProxyData(
appWallet.id,
proxies,
appWallet.scriptCbor,
network.toString(),
false // Use cache to avoid duplicate requests
);
console.log("WalletDataLoader: Successfully fetched all proxy data");
}
} catch (error) {
console.error("WalletDataLoader: Error fetching proxy data:", error);
Expand Down Expand Up @@ -144,10 +114,6 @@ export default function WalletDataLoader() {
if (appWallet && walletsUtxos[appWallet?.id] === undefined) {
console.log("WalletDataLoader: Calling refreshWallet");
refreshWallet();
} else if (appWallet) {
// If wallet exists but we already have UTxOs, still fetch proxy data
console.log("WalletDataLoader: Calling fetchProxyData directly");
fetchProxyData();
}
}, [appWallet]);

Expand Down
64 changes: 14 additions & 50 deletions src/components/multisig/proxy/ProxyControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -402,31 +402,12 @@ export default function ProxyControl() {
}
}, [network, wallet, appWallet?.scriptCbor]);

// Fetch all proxy balances for TVL calculation
// Fetch all proxy balances for TVL calculation (now handled globally)
const fetchAllProxyBalances = useCallback(async () => {
if (!proxies || proxies.length === 0 || !proxyContract) return;

try {
setTvlLoading(true);
const balances: Record<string, Array<{ unit: string; quantity: string }>> = {};

for (const proxy of proxies) {
try {
const balance = await getProxyBalance(proxy.proxyAddress);
balances[proxy.id] = balance;
} catch (error) {
console.error(`Failed to fetch balance for proxy ${proxy.id}:`, error);
balances[proxy.id] = [];
}
}

// Balances handled elsewhere
} catch (error) {
console.error("Failed to fetch proxy balances:", error);
} finally {
setTvlLoading(false);
}
}, [proxies, proxyContract, getProxyBalance]);
// This function is now handled globally by WalletDataLoaderWrapper
// to avoid duplicate API calls. The proxy store already contains the balance data.
console.log("ProxyControl: fetchAllProxyBalances called but data is handled globally");
}, []);

// Calculate Total Value Locked (TVL) across all proxies
const calculateTVL = useCallback(() => {
Expand Down Expand Up @@ -456,37 +437,20 @@ export default function ProxyControl() {

const { totalADA, totalAssets } = calculateTVL();

// Fetch all proxy balances when proxies change
useEffect(() => {
if (proxies && proxies.length > 0 && proxyContract) {
void fetchAllProxyBalances();
}
}, [proxies, proxyContract, fetchAllProxyBalances]);

// Refresh balances when component mounts or wallet changes
useEffect(() => {
if (proxies && proxies.length > 0 && proxyContract && connected) {
// Small delay to ensure everything is initialized
const timer = setTimeout(() => {
void fetchAllProxyBalances();
}, 1000);
return () => clearTimeout(timer);
}
}, [connected, proxyContract, fetchAllProxyBalances, proxies]);
// Proxy balance data is now handled globally by WalletDataLoaderWrapper
// No need to fetch balances here as they're already in the proxy store

// Manual TVL refresh function
const refreshTVL = useCallback(async () => {
if (proxies && proxies.length > 0 && proxyContract) {
await fetchAllProxyBalances();
}
}, [proxies, proxyContract, fetchAllProxyBalances]);
// TVL is calculated from proxy store data, no need to fetch
console.log("ProxyControl: refreshTVL called - data comes from proxy store");
}, []);

// Global refresh function for all proxy balances (unused but kept for potential future use)
// Global refresh function for all proxy balances (now handled globally)
const refreshAllBalances = useCallback(async () => {
if (proxies && proxies.length > 0 && proxyContract) {
void fetchAllProxyBalances();
}
}, [proxies, proxyContract, fetchAllProxyBalances]);
// Balance data is now handled globally by WalletDataLoaderWrapper
console.log("ProxyControl: refreshAllBalances called - data handled globally");
}, []);

// Spend outputs management
const handleSpendOutputsChange = useCallback((outputs: ProxyOutput[]) => {
Expand Down
103 changes: 103 additions & 0 deletions src/lib/zustand/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ interface ProxyState {
fetchProxyBalance: (walletId: string, proxyId: string, proxyAddress: string, network: string) => Promise<void>;
fetchProxyDrepInfo: (walletId: string, proxyId: string, proxyAddress: string, authTokenId: string, scriptCbor: string, network: string, paramUtxo: string, forceRefresh?: boolean) => Promise<void>;
fetchProxyDelegatorsInfo: (walletId: string, proxyId: string, proxyAddress: string, authTokenId: string, scriptCbor: string, network: string, paramUtxo: string, forceRefresh?: boolean) => Promise<void>;
fetchAllProxyData: (walletId: string, proxies: ProxyData[], scriptCbor: string, network: string, forceRefresh?: boolean) => Promise<void>;

// Utility actions
updateProxyData: (walletId: string, proxyId: string, updates: Partial<ProxyData>) => void;
Expand Down Expand Up @@ -249,6 +250,106 @@ export const useProxyStore = create<ProxyState>()(
get().setDrepLoading(proxyId, false);
}
},

// Fetch all proxy data in parallel
fetchAllProxyData: async (walletId, proxies, scriptCbor, network, forceRefresh = false) => {
try {
// Prevent multiple simultaneous fetches for the same wallet
const currentState = get();
if (currentState.loading[walletId]) {
console.log(`Proxy data already loading for wallet ${walletId}, skipping...`);
return;
}

// Check if data is fresh (less than 30 seconds old) and skip if not forcing refresh
if (!forceRefresh && currentState.proxies[walletId]) {
const oldestUpdate = Math.min(
...currentState.proxies[walletId].map(p => p.lastUpdated || 0)
);
const isDataFresh = oldestUpdate > 0 && (Date.now() - oldestUpdate) < 30000; // 30 seconds
if (isDataFresh) {
console.log(`Proxy data is fresh (less than 30s old) for wallet ${walletId}, skipping...`);
return;
}
}

get().setLoading(walletId, true);
get().setError(walletId, null);

// Create a single txBuilder instance to reuse across all proxies
const txBuilder = getTxBuilder(parseInt(network));

// Create all fetch promises in parallel
const fetchPromises = proxies.map(async (proxy) => {
try {
// Set loading state for this proxy
get().setDrepLoading(proxy.id, true);
get().setDrepError(proxy.id, null);

// Reuse the same txBuilder instance for all proxies
const proxyContract = new MeshProxyContract(
{
mesh: txBuilder,
wallet: undefined,
networkId: parseInt(network),
},
{
paramUtxo: JSON.parse(proxy.paramUtxo || '{}'),
},
scriptCbor,
);
proxyContract.proxyAddress = proxy.proxyAddress;

// Fetch all data for this proxy in parallel
const [balance, drepStatus, delegators] = await Promise.allSettled([
proxyContract.getProxyBalance(),
proxyContract.getDrepStatus(forceRefresh),
proxyContract.getDrepDelegators(forceRefresh),
]);

// Get DRep ID
const drepId = proxyContract.getDrepId();

// Process results
const drepInfo: ProxyDrepInfo | undefined = drepStatus.status === 'fulfilled' ? drepStatus.value : undefined;
const delegatorsInfo: ProxyDelegatorsInfo | undefined = delegators.status === 'fulfilled' ? (delegators.value as ProxyDelegatorsInfo) : undefined;

// Update the specific proxy's data
const currentState = get();
const updatedProxies = currentState.proxies[walletId]?.map(p =>
p.id === proxy.id
? {
...p,
balance: balance.status === 'fulfilled' ? balance.value : p.balance,
drepId,
drepInfo,
delegatorsInfo,
lastUpdated: Date.now()
}
: p
) || [];

set((state) => ({
proxies: { ...state.proxies, [walletId]: updatedProxies },
drepLoading: { ...state.drepLoading, [proxy.id]: false },
drepErrors: { ...state.drepErrors, [proxy.id]: null },
}));

} catch (error) {
get().setDrepError(proxy.id, `Failed to fetch data for proxy ${proxy.id}`);
get().setDrepLoading(proxy.id, false);
}
});

// Wait for all proxy data to be fetched
await Promise.allSettled(fetchPromises);

get().setLoading(walletId, false);
} catch (error) {
get().setError(walletId, `Failed to fetch proxy data: ${error instanceof Error ? error.message : 'Unknown error'}`);
get().setLoading(walletId, false);
}
},

// Update specific proxy data
updateProxyData: (walletId, proxyId, updates) =>
Expand Down Expand Up @@ -324,6 +425,7 @@ export const useProxyActions = () => {
const fetchProxyBalance = useProxyStore((state) => state.fetchProxyBalance);
const fetchProxyDrepInfo = useProxyStore((state) => state.fetchProxyDrepInfo);
const fetchProxyDelegatorsInfo = useProxyStore((state) => state.fetchProxyDelegatorsInfo);
const fetchAllProxyData = useProxyStore((state) => state.fetchAllProxyData);
const updateProxyData = useProxyStore((state) => state.updateProxyData);
const clearProxyData = useProxyStore((state) => state.clearProxyData);

Expand All @@ -336,6 +438,7 @@ export const useProxyActions = () => {
fetchProxyBalance,
fetchProxyDrepInfo,
fetchProxyDelegatorsInfo,
fetchAllProxyData,
updateProxyData,
clearProxyData,
};
Expand Down