Skip to content

Commit 829cc1f

Browse files
authored
Merge branch 'main' into feature/create-new-wallet-updates
2 parents 13d9b02 + 66f06b4 commit 829cc1f

File tree

168 files changed

+35445
-4087
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+35445
-4087
lines changed

package-lock.json

Lines changed: 1219 additions & 3903 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"@meshsdk/core-cst": "^1.9.0-beta.77",
3030
"@meshsdk/react": "^1.9.0-beta.77",
3131
"@octokit/core": "^6.1.2",
32-
"@prisma/client": "^6.4.1",
32+
"@prisma/client": "^6.17.1",
3333
"@radix-ui/react-accordion": "^1.2.0",
3434
"@radix-ui/react-checkbox": "^1.1.1",
3535
"@radix-ui/react-collapsible": "^1.1.0",
@@ -111,7 +111,7 @@
111111
"postcss": "^8.4.39",
112112
"prettier": "^3.3.2",
113113
"prettier-plugin-tailwindcss": "^0.6.5",
114-
"prisma": "^6.4.1",
114+
"prisma": "^6.17.1",
115115
"tailwindcss": "^3.4.3",
116116
"ts-jest": "^29.4.4",
117117
"typescript": "^5.5.3"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- AlterTable
2+
ALTER TABLE "Wallet" ADD COLUMN "migrationTargetWalletId" TEXT;
3+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- CreateTable
2+
CREATE TABLE "Proxy" (
3+
"id" TEXT NOT NULL,
4+
"walletId" TEXT,
5+
"proxyAddress" TEXT NOT NULL,
6+
"authTokenId" TEXT NOT NULL,
7+
"paramUtxo" TEXT NOT NULL,
8+
"description" TEXT,
9+
"isActive" BOOLEAN NOT NULL DEFAULT true,
10+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
11+
"updatedAt" TIMESTAMP(3) NOT NULL,
12+
"userId" TEXT,
13+
14+
CONSTRAINT "Proxy_pkey" PRIMARY KEY ("id")
15+
);
16+
17+
18+
-- CreateTable
19+
CREATE TABLE "Migration" (
20+
"id" TEXT NOT NULL,
21+
"originalWalletId" TEXT NOT NULL,
22+
"newWalletId" TEXT,
23+
"ownerAddress" TEXT NOT NULL,
24+
"currentStep" INTEGER NOT NULL DEFAULT 0,
25+
"status" TEXT NOT NULL DEFAULT 'pending',
26+
"migrationData" JSONB,
27+
"errorMessage" TEXT,
28+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
29+
"updatedAt" TIMESTAMP(3) NOT NULL,
30+
"completedAt" TIMESTAMP(3),
31+
32+
CONSTRAINT "Migration_pkey" PRIMARY KEY ("id")
33+
);
34+
35+
-- CreateIndex
36+
CREATE INDEX "Migration_ownerAddress_idx" ON "Migration"("ownerAddress");
37+
38+
-- CreateIndex
39+
CREATE INDEX "Migration_originalWalletId_idx" ON "Migration"("originalWalletId");
40+
41+
-- CreateIndex
42+
CREATE INDEX "Migration_status_idx" ON "Migration"("status");
43+
44+
-- CreateIndex
45+
CREATE INDEX "Migration_createdAt_idx" ON "Migration"("createdAt");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Please do not edit this file manually
22
# It should be added in your version-control system (e.g., Git)
3-
provider = "postgresql"
3+
provider = "postgresql"

prisma/schema.prisma

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// This is your Prisma schema file,
2-
// learn more about it in the docs: https://pris.ly/d/prisma-schema
3-
41
generator client {
52
provider = "prisma-client-js"
63
}
@@ -20,21 +17,21 @@ model User {
2017
}
2118

2219
model Wallet {
23-
id String @id @default(cuid())
24-
name String
25-
description String?
26-
signersAddresses String[]
27-
signersStakeKeys String[]
28-
signersDRepKeys String[]
29-
signersDescriptions String[]
30-
numRequiredSigners Int?
31-
verified String[]
32-
scriptCbor String
33-
stakeCredentialHash String?
34-
type String
35-
isArchived Boolean @default(false)
36-
clarityApiKey String?
37-
rawImportBodies Json?
20+
id String @id @default(cuid())
21+
name String
22+
description String?
23+
signersAddresses String[]
24+
signersStakeKeys String[]
25+
signersDRepKeys String[]
26+
signersDescriptions String[]
27+
numRequiredSigners Int?
28+
verified String[]
29+
scriptCbor String
30+
stakeCredentialHash String?
31+
type String
32+
isArchived Boolean @default(false)
33+
clarityApiKey String?
34+
migrationTargetWalletId String?
3835
}
3936

4037
model Transaction {
@@ -103,6 +100,19 @@ model Ballot {
103100
createdAt DateTime @default(now())
104101
}
105102

103+
model Proxy {
104+
id String @id @default(cuid())
105+
walletId String?
106+
proxyAddress String
107+
authTokenId String
108+
paramUtxo String
109+
description String?
110+
isActive Boolean @default(true)
111+
createdAt DateTime @default(now())
112+
updatedAt DateTime @updatedAt
113+
userId String?
114+
}
115+
106116
model BalanceSnapshot {
107117
id String @id @default(cuid())
108118
walletId String
@@ -113,3 +123,23 @@ model BalanceSnapshot {
113123
isArchived Boolean
114124
snapshotDate DateTime @default(now())
115125
}
126+
127+
model Migration {
128+
id String @id @default(cuid())
129+
originalWalletId String // The wallet being migrated from
130+
newWalletId String? // The new wallet being created (null until created)
131+
ownerAddress String // The user who initiated the migration
132+
currentStep Int @default(0) // 0=pre-checks, 1=create wallet, 2=proxy setup, 3=transfer funds, 4=complete
133+
status String @default("pending") // pending, in_progress, completed, failed, cancelled
134+
migrationData Json? // Store any additional migration-specific data
135+
errorMessage String? // Store error details if migration fails
136+
createdAt DateTime @default(now())
137+
updatedAt DateTime @updatedAt
138+
completedAt DateTime?
139+
140+
// Indexes for efficient querying
141+
@@index([ownerAddress])
142+
@@index([originalWalletId])
143+
@@index([status])
144+
@@index([createdAt])
145+
}

src/components/common/overall-layout/layout.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,19 @@ export default function RootLayout({
161161
return;
162162
}
163163

164+
// Handle connection errors with retry logic
165+
if (error instanceof Error && (
166+
error.message.includes("Could not establish connection") ||
167+
error.message.includes("Receiving end does not exist") ||
168+
error.message.includes("Cannot read properties of undefined")
169+
)) {
170+
console.log("Browser extension connection error detected, retrying in 2 seconds...");
171+
setTimeout(() => {
172+
window.location.reload();
173+
}, 2000);
174+
return;
175+
}
176+
164177
// For other errors, don't throw to prevent app crash
165178
// The user can retry by reconnecting their wallet
166179
}
@@ -296,6 +309,7 @@ export default function RootLayout({
296309
</div>
297310
</header>
298311

312+
299313
<main className="relative flex flex-1 flex-col gap-4 overflow-y-auto overflow-x-hidden p-4 md:p-8">
300314
<WalletErrorBoundary
301315
fallback={

src/components/common/overall-layout/mobile-wrappers/wallet-data-loader-wrapper.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Asset } from "@meshsdk/core";
1111
import { getDRepIds } from "@meshsdk/core-cst";
1212
import { BlockfrostDrepInfo } from "@/types/governance";
1313
import { Button } from "@/components/ui/button";
14+
import { useProxyActions } from "@/lib/zustand/proxy";
1415

1516
interface WalletDataLoaderWrapperProps {
1617
mode: "button" | "menu-item";
@@ -47,6 +48,7 @@ export default function WalletDataLoaderWrapper({
4748
const setWalletAssetMetadata = useWalletsStore(
4849
(state) => state.setWalletAssetMetadata,
4950
);
51+
const { fetchAllProxyData, setProxies } = useProxyActions();
5052

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

@@ -179,6 +181,35 @@ export default function WalletDataLoaderWrapper({
179181
}
180182
}
181183

184+
async function fetchProxyData() {
185+
if (appWallet?.id && appWallet?.scriptCbor) {
186+
try {
187+
// Get proxies from API
188+
const proxies = await ctx.proxy.getProxiesByUserOrWallet.fetch({
189+
walletId: appWallet.id,
190+
});
191+
192+
// First, add proxies to the store
193+
setProxies(appWallet.id, proxies);
194+
195+
// Fetch all proxy data in parallel using the new batch function
196+
if (proxies.length > 0) {
197+
console.log(`WalletDataLoaderWrapper: Fetching data for ${proxies.length} proxies in parallel`);
198+
await fetchAllProxyData(
199+
appWallet.id,
200+
proxies,
201+
appWallet.scriptCbor,
202+
network.toString(),
203+
false // Use cache to avoid duplicate requests
204+
);
205+
console.log("WalletDataLoaderWrapper: Successfully fetched all proxy data");
206+
}
207+
} catch (error) {
208+
console.error("WalletDataLoaderWrapper: Error fetching proxy data:", error);
209+
}
210+
}
211+
}
212+
182213
async function refreshWallet() {
183214
if (fetchingTransactions.current) return;
184215

@@ -188,8 +219,11 @@ export default function WalletDataLoaderWrapper({
188219
await getTransactionsOnChain();
189220
await getWalletAssets();
190221
await getDRepInfo();
222+
await fetchProxyData(); // Fetch proxy data
191223
void ctx.transaction.getPendingTransactions.invalidate();
192224
void ctx.transaction.getAllTransactions.invalidate();
225+
// Also refresh proxy data
226+
void ctx.proxy.getProxiesByUserOrWallet.invalidate();
193227
setRandomState();
194228
setLoading(false);
195229
fetchingTransactions.current = false;
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { useEffect } from "react";
2+
import useAppWallet from "@/hooks/useAppWallet";
3+
import { useProxyData, useProxyActions } from "@/lib/zustand/proxy";
4+
import { useSiteStore } from "@/lib/zustand/site";
5+
import { api } from "@/utils/api";
6+
7+
export default function ProxyDataLoader() {
8+
const { appWallet } = useAppWallet();
9+
const network = useSiteStore((state) => state.network);
10+
const { proxies } = useProxyData(appWallet?.id);
11+
const {
12+
setProxies,
13+
fetchProxyBalance,
14+
fetchProxyDrepInfo,
15+
fetchProxyDelegatorsInfo,
16+
clearProxyData
17+
} = useProxyActions();
18+
19+
20+
21+
// Get proxies from API
22+
const { data: apiProxies, refetch: refetchProxies } = api.proxy.getProxiesByUserOrWallet.useQuery(
23+
{
24+
walletId: appWallet?.id ?? undefined,
25+
},
26+
{
27+
enabled: !!appWallet?.id,
28+
refetchOnWindowFocus: false,
29+
staleTime: 30000, // 30 seconds
30+
}
31+
);
32+
33+
// Update store when API data changes
34+
useEffect(() => {
35+
if (apiProxies && appWallet?.id) {
36+
const proxyData = apiProxies.map(proxy => ({
37+
id: proxy.id,
38+
proxyAddress: proxy.proxyAddress,
39+
authTokenId: proxy.authTokenId,
40+
paramUtxo: proxy.paramUtxo,
41+
description: proxy.description,
42+
isActive: proxy.isActive,
43+
createdAt: new Date(proxy.createdAt),
44+
lastUpdated: Date.now(),
45+
}));
46+
47+
setProxies(appWallet.id, proxyData);
48+
}
49+
}, [apiProxies, appWallet?.id, setProxies]);
50+
51+
// Fetch additional data for each proxy
52+
useEffect(() => {
53+
if (proxies.length > 0 && appWallet?.id && appWallet?.scriptCbor) {
54+
void (async () => {
55+
for (const proxy of proxies) {
56+
// Only fetch if we don't have recent data (older than 5 minutes)
57+
const isStale = !proxy.lastUpdated || (Date.now() - proxy.lastUpdated) > 5 * 60 * 1000;
58+
if (isStale) {
59+
try {
60+
await fetchProxyBalance(appWallet.id, proxy.id, proxy.proxyAddress, network.toString());
61+
await fetchProxyDrepInfo(
62+
appWallet.id,
63+
proxy.id,
64+
proxy.proxyAddress,
65+
proxy.authTokenId,
66+
appWallet.scriptCbor,
67+
network.toString(),
68+
proxy.paramUtxo,
69+
true,
70+
);
71+
await fetchProxyDelegatorsInfo(
72+
appWallet.id,
73+
proxy.id,
74+
proxy.proxyAddress,
75+
proxy.authTokenId,
76+
appWallet.scriptCbor,
77+
network.toString(),
78+
proxy.paramUtxo,
79+
true,
80+
);
81+
} catch (error) {
82+
console.error(`Error fetching data for proxy ${proxy.id}:`, error);
83+
}
84+
}
85+
}
86+
})();
87+
}
88+
}, [proxies, appWallet?.id, appWallet?.scriptCbor, network, fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo]);
89+
90+
// Clear proxy data when wallet changes
91+
useEffect(() => {
92+
return () => {
93+
if (appWallet?.id) {
94+
clearProxyData(appWallet.id);
95+
}
96+
};
97+
}, [appWallet?.id, clearProxyData]);
98+
99+
// Expose refetch function for manual refresh
100+
useEffect(() => {
101+
// Store refetch function in window for global access if needed
102+
if (typeof window !== 'undefined') {
103+
const w = window as Window & { refetchProxyData?: () => void };
104+
w.refetchProxyData = () => {
105+
void refetchProxies();
106+
};
107+
}
108+
}, [refetchProxies]);
109+
110+
return null; // This is a data loader component, no UI
111+
}

0 commit comments

Comments
 (0)