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
5,122 changes: 1,219 additions & 3,903 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@meshsdk/core-cst": "^1.9.0-beta.77",
"@meshsdk/react": "^1.9.0-beta.77",
"@octokit/core": "^6.1.2",
"@prisma/client": "^6.4.1",
"@prisma/client": "^6.17.1",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.0",
Expand Down Expand Up @@ -111,7 +111,7 @@
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"prisma": "^6.4.1",
"prisma": "^6.17.1",
"tailwindcss": "^3.4.3",
"ts-jest": "^29.4.4",
"typescript": "^5.5.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Wallet" ADD COLUMN "migrationTargetWalletId" TEXT;

65 changes: 48 additions & 17 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
provider = "prisma-client-js"
}
Expand All @@ -20,20 +17,21 @@ model User {
}

model Wallet {
id String @id @default(cuid())
name String
description String?
signersAddresses String[]
signersStakeKeys String[]
signersDRepKeys String[]
signersDescriptions String[]
numRequiredSigners Int?
verified String[]
scriptCbor String
stakeCredentialHash String?
type String
isArchived Boolean @default(false)
clarityApiKey String?
id String @id @default(cuid())
name String
description String?
signersAddresses String[]
signersStakeKeys String[]
signersDRepKeys String[]
signersDescriptions String[]
numRequiredSigners Int?
verified String[]
scriptCbor String
stakeCredentialHash String?
type String
isArchived Boolean @default(false)
clarityApiKey String?
migrationTargetWalletId String?
}

model Transaction {
Expand Down Expand Up @@ -102,6 +100,19 @@ model Ballot {
createdAt DateTime @default(now())
}

model Proxy {
id String @id @default(cuid())
walletId String?
proxyAddress String
authTokenId String
paramUtxo String
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String?
}

model BalanceSnapshot {
id String @id @default(cuid())
walletId String
Expand All @@ -112,3 +123,23 @@ model BalanceSnapshot {
isArchived Boolean
snapshotDate DateTime @default(now())
}

model Migration {
id String @id @default(cuid())
originalWalletId String // The wallet being migrated from
newWalletId String? // The new wallet being created (null until created)
ownerAddress String // The user who initiated the migration
currentStep Int @default(0) // 0=pre-checks, 1=create wallet, 2=proxy setup, 3=transfer funds, 4=complete
status String @default("pending") // pending, in_progress, completed, failed, cancelled
migrationData Json? // Store any additional migration-specific data
errorMessage String? // Store error details if migration fails
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
completedAt DateTime?

// Indexes for efficient querying
@@index([ownerAddress])
@@index([originalWalletId])
@@index([status])
@@index([createdAt])
}
1 change: 1 addition & 0 deletions src/components/common/overall-layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export default function RootLayout({
</div>
</header>


<main className="relative flex flex-1 flex-col gap-4 overflow-y-auto overflow-x-hidden p-4 md:p-8">
<WalletErrorBoundary
fallback={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Asset } from "@meshsdk/core";
import { getDRepIds } from "@meshsdk/core-cst";
import { BlockfrostDrepInfo } from "@/types/governance";
import { Button } from "@/components/ui/button";
import { useProxyActions } from "@/lib/zustand/proxy";

interface WalletDataLoaderWrapperProps {
mode: "button" | "menu-item";
Expand Down Expand Up @@ -47,6 +48,7 @@ export default function WalletDataLoaderWrapper({
const setWalletAssetMetadata = useWalletsStore(
(state) => state.setWalletAssetMetadata,
);
const { fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo, setProxies } = useProxyActions();

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

Expand Down Expand Up @@ -179,6 +181,64 @@ export default function WalletDataLoaderWrapper({
}
}

async function fetchProxyData() {
if (appWallet?.id && appWallet?.scriptCbor) {
try {
// Get proxies from API
const proxies = await ctx.proxy.getProxiesByUserOrWallet.fetch({
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);
}
}
} catch (error) {
console.error("WalletDataLoaderWrapper: Error fetching proxy data:", error);
}
}
}

async function refreshWallet() {
if (fetchingTransactions.current) return;

Expand All @@ -188,8 +248,11 @@ export default function WalletDataLoaderWrapper({
await getTransactionsOnChain();
await getWalletAssets();
await getDRepInfo();
await fetchProxyData(); // Fetch proxy data
void ctx.transaction.getPendingTransactions.invalidate();
void ctx.transaction.getAllTransactions.invalidate();
// Also refresh proxy data
void ctx.proxy.getProxiesByUserOrWallet.invalidate();
setRandomState();
setLoading(false);
fetchingTransactions.current = false;
Expand All @@ -204,6 +267,9 @@ 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
111 changes: 111 additions & 0 deletions src/components/common/overall-layout/proxy-data-loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useEffect } from "react";
import useAppWallet from "@/hooks/useAppWallet";
import { useProxyData, useProxyActions } from "@/lib/zustand/proxy";
import { useSiteStore } from "@/lib/zustand/site";
import { api } from "@/utils/api";

export default function ProxyDataLoader() {
const { appWallet } = useAppWallet();
const network = useSiteStore((state) => state.network);
const { proxies } = useProxyData(appWallet?.id);
const {
setProxies,
fetchProxyBalance,
fetchProxyDrepInfo,
fetchProxyDelegatorsInfo,
clearProxyData
} = useProxyActions();



// Get proxies from API
const { data: apiProxies, refetch: refetchProxies } = api.proxy.getProxiesByUserOrWallet.useQuery(
{
walletId: appWallet?.id ?? undefined,
},
{
enabled: !!appWallet?.id,
refetchOnWindowFocus: false,
staleTime: 30000, // 30 seconds
}
);

// Update store when API data changes
useEffect(() => {
if (apiProxies && appWallet?.id) {
const proxyData = apiProxies.map(proxy => ({
id: proxy.id,
proxyAddress: proxy.proxyAddress,
authTokenId: proxy.authTokenId,
paramUtxo: proxy.paramUtxo,
description: proxy.description,
isActive: proxy.isActive,
createdAt: new Date(proxy.createdAt),
lastUpdated: Date.now(),
}));

setProxies(appWallet.id, proxyData);
}
}, [apiProxies, appWallet?.id, setProxies]);

// Fetch additional data for each proxy
useEffect(() => {
if (proxies.length > 0 && appWallet?.id && appWallet?.scriptCbor) {
void (async () => {
for (const proxy of proxies) {
// Only fetch if we don't have recent data (older than 5 minutes)
const isStale = !proxy.lastUpdated || (Date.now() - proxy.lastUpdated) > 5 * 60 * 1000;
if (isStale) {
try {
await fetchProxyBalance(appWallet.id, proxy.id, proxy.proxyAddress, network.toString());
await fetchProxyDrepInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true,
);
await fetchProxyDelegatorsInfo(
appWallet.id,
proxy.id,
proxy.proxyAddress,
proxy.authTokenId,
appWallet.scriptCbor,
network.toString(),
proxy.paramUtxo,
true,
);
} catch (error) {
console.error(`Error fetching data for proxy ${proxy.id}:`, error);
}
}
}
})();
}
}, [proxies, appWallet?.id, appWallet?.scriptCbor, network, fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo]);

// Clear proxy data when wallet changes
useEffect(() => {
return () => {
if (appWallet?.id) {
clearProxyData(appWallet.id);
}
};
}, [appWallet?.id, clearProxyData]);

// Expose refetch function for manual refresh
useEffect(() => {
// Store refetch function in window for global access if needed
if (typeof window !== 'undefined') {
const w = window as Window & { refetchProxyData?: () => void };
w.refetchProxyData = () => {
void refetchProxies();
};
}
}, [refetchProxies]);

return null; // This is a data loader component, no UI
}
Loading