Skip to content

Commit be8d98e

Browse files
authored
Merge pull request #156 from MeshJS/feature/aiken-multisig
Feature/aiken multisig
2 parents de3e317 + ec0f050 commit be8d98e

File tree

165 files changed

+35365
-4079
lines changed

Some content is hidden

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

165 files changed

+35365
-4079
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+

prisma/schema.prisma

Lines changed: 48 additions & 17 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,20 +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?
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?
3735
}
3836

3937
model Transaction {
@@ -102,6 +100,19 @@ model Ballot {
102100
createdAt DateTime @default(now())
103101
}
104102

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+
105116
model BalanceSnapshot {
106117
id String @id @default(cuid())
107118
walletId String
@@ -112,3 +123,23 @@ model BalanceSnapshot {
112123
isArchived Boolean
113124
snapshotDate DateTime @default(now())
114125
}
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ export default function RootLayout({
296296
</div>
297297
</header>
298298

299+
299300
<main className="relative flex flex-1 flex-col gap-4 overflow-y-auto overflow-x-hidden p-4 md:p-8">
300301
<WalletErrorBoundary
301302
fallback={

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

Lines changed: 66 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 { fetchProxyBalance, fetchProxyDrepInfo, fetchProxyDelegatorsInfo, setProxies } = useProxyActions();
5052

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

@@ -179,6 +181,64 @@ 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+
193+
// First, add proxies to the store
194+
setProxies(appWallet.id, proxies);
195+
196+
// Fetch balance and DRep info for each proxy
197+
for (const proxy of proxies) {
198+
try {
199+
200+
// Fetch balance
201+
await fetchProxyBalance(
202+
appWallet.id,
203+
proxy.id,
204+
proxy.proxyAddress,
205+
network.toString()
206+
);
207+
208+
// Fetch DRep info with force refresh
209+
await fetchProxyDrepInfo(
210+
appWallet.id,
211+
proxy.id,
212+
proxy.proxyAddress,
213+
proxy.authTokenId,
214+
appWallet.scriptCbor,
215+
network.toString(),
216+
proxy.paramUtxo,
217+
true // Force refresh to bypass cache
218+
);
219+
220+
// Fetch delegators info with force refresh
221+
await fetchProxyDelegatorsInfo(
222+
appWallet.id,
223+
proxy.id,
224+
proxy.proxyAddress,
225+
proxy.authTokenId,
226+
appWallet.scriptCbor,
227+
network.toString(),
228+
proxy.paramUtxo,
229+
true // Force refresh to bypass cache
230+
);
231+
232+
} catch (error) {
233+
console.error(`WalletDataLoaderWrapper: Error fetching data for proxy ${proxy.id}:`, error);
234+
}
235+
}
236+
} catch (error) {
237+
console.error("WalletDataLoaderWrapper: Error fetching proxy data:", error);
238+
}
239+
}
240+
}
241+
182242
async function refreshWallet() {
183243
if (fetchingTransactions.current) return;
184244

@@ -188,8 +248,11 @@ export default function WalletDataLoaderWrapper({
188248
await getTransactionsOnChain();
189249
await getWalletAssets();
190250
await getDRepInfo();
251+
await fetchProxyData(); // Fetch proxy data
191252
void ctx.transaction.getPendingTransactions.invalidate();
192253
void ctx.transaction.getAllTransactions.invalidate();
254+
// Also refresh proxy data
255+
void ctx.proxy.getProxiesByUserOrWallet.invalidate();
193256
setRandomState();
194257
setLoading(false);
195258
fetchingTransactions.current = false;
@@ -204,6 +267,9 @@ export default function WalletDataLoaderWrapper({
204267
if (appWallet && prevWalletIdRef.current !== appWallet.id) {
205268
refreshWallet();
206269
prevWalletIdRef.current = appWallet.id;
270+
} else if (appWallet) {
271+
// If wallet exists but we already have data, still fetch proxy data
272+
fetchProxyData();
207273
}
208274
}, [appWallet]);
209275

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)