Skip to content

Commit bbbfa3a

Browse files
authored
Merge pull request #141 from MeshJS/add-drepkeys
Add drepkeys
2 parents ba0eee5 + 516bfd3 commit bbbfa3a

File tree

38 files changed

+3194
-749
lines changed

38 files changed

+3194
-749
lines changed

package-lock.json

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

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
"@auth/prisma-adapter": "^1.6.0",
2525
"@hookform/resolvers": "^3.9.0",
2626
"@jinglescode/nostr-chat-plugin": "^0.0.11",
27-
"@meshsdk/core": "^1.9.0-beta.18",
28-
"@meshsdk/core-csl": "^1.9.0-beta.18",
29-
"@meshsdk/core-cst": "^1.9.0-beta.19",
30-
"@meshsdk/react": "^1.9.0-beta.18",
27+
"@meshsdk/core": "^1.9.0-beta.77",
28+
"@meshsdk/core-csl": "^1.9.0-beta.77",
29+
"@meshsdk/core-cst": "^1.9.0-beta.77",
30+
"@meshsdk/react": "^1.9.0-beta.77",
3131
"@octokit/core": "^6.1.2",
3232
"@prisma/client": "^6.4.1",
3333
"@radix-ui/react-accordion": "^1.2.0",
@@ -60,14 +60,14 @@
6060
"clsx": "^2.1.1",
6161
"cors": "^2.8.5",
6262
"dexie": "^4.0.11",
63-
"formidable": "^3.5.2",
63+
"formidable": "^3.5.4",
6464
"framer-motion": "^11.11.9",
6565
"geist": "^1.3.0",
6666
"idb-keyval": "^6.2.1",
6767
"jsonld": "^8.3.3",
6868
"jsonwebtoken": "^9.0.2",
6969
"lucide-react": "^0.439.0",
70-
"next": "^14.2.4",
70+
"next": "^15.2.4",
7171
"next-auth": "^4.24.7",
7272
"papaparse": "^5.5.3",
7373
"react": "^18.3.1",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- AlterTable
2+
ALTER TABLE "User" ADD COLUMN "drepKeyHash" TEXT NOT NULL DEFAULT '';
3+
4+
-- AlterTable
5+
ALTER TABLE "Wallet" ADD COLUMN "signersDRepKeys" TEXT[];
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "NewWallet" ADD COLUMN "signersDRepKeys" TEXT[];

prisma/schema.prisma

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ model User {
1414
id String @id @default(cuid())
1515
address String @unique
1616
stakeAddress String @unique
17+
drepKeyHash String @default("")
1718
nostrKey String @unique
1819
discordId String @default("")
1920
}
@@ -24,6 +25,7 @@ model Wallet {
2425
description String?
2526
signersAddresses String[]
2627
signersStakeKeys String[]
28+
signersDRepKeys String[]
2729
signersDescriptions String[]
2830
numRequiredSigners Int?
2931
verified String[]
@@ -70,6 +72,7 @@ model NewWallet {
7072
description String?
7173
signersAddresses String[]
7274
signersStakeKeys String[]
75+
signersDRepKeys String[]
7376
signersDescriptions String[]
7477
numRequiredSigners Int?
7578
ownerAddress String

src/components/common/card-content.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function CardUI({
1717
headerDom?: ReactNode;
1818
}) {
1919
return (
20-
<Card className={`self-start ${cardClassName}`}>
20+
<Card className={`w-full max-w-4xl ${cardClassName}`}>
2121
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
2222
<CardTitle className="text-xl font-medium">{title}</CardTitle>
2323
{headerDom && headerDom}
@@ -33,7 +33,7 @@ export default function CardUI({
3333
</>
3434
)}
3535
</CardHeader>
36-
<CardContent>
36+
<CardContent className="overflow-y-auto max-h-[calc(100vh-200px)]">
3737
<div className="mt-1 flex flex-col gap-2">
3838
{description && (
3939
<p className="text-sm text-muted-foreground">{description}</p>

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

Lines changed: 128 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import useUser from "@/hooks/useUser";
99
import { useUserStore } from "@/lib/zustand/user";
1010
import useAppWallet from "@/hooks/useAppWallet";
1111

12-
import SessionProvider from "@/components/SessionProvider"
12+
import SessionProvider from "@/components/SessionProvider";
1313
import { getServerSession } from "next-auth";
1414

1515
import MenuWallets from "@/components/common/overall-layout/menus/wallets";
@@ -35,26 +35,33 @@ import {
3535
BreadcrumbSeparator,
3636
} from "@/components/ui/breadcrumb";
3737

38-
// Simple error boundary component
39-
class SimpleErrorBoundary extends Component<
38+
// Enhanced error boundary component for wallet errors
39+
class WalletErrorBoundary extends Component<
4040
{ children: ReactNode; fallback: ReactNode },
41-
{ hasError: boolean }
41+
{ hasError: boolean; error: Error | null }
4242
> {
4343
constructor(props: { children: ReactNode; fallback: ReactNode }) {
4444
super(props);
45-
this.state = { hasError: false };
45+
this.state = { hasError: false, error: null };
4646
}
4747

48-
static getDerivedStateFromError() {
49-
return { hasError: true };
48+
static getDerivedStateFromError(error: Error) {
49+
return { hasError: true, error };
5050
}
5151

5252
componentDidCatch(error: Error, errorInfo: any) {
53-
console.error('Error caught by boundary:', error, errorInfo);
53+
console.error('Error caught by wallet boundary:', error, errorInfo);
54+
55+
// Handle specific wallet errors
56+
if (error.message.includes("account changed")) {
57+
console.log("Wallet account changed error caught by boundary, reloading page...");
58+
window.location.reload();
59+
return;
60+
}
5461
}
5562

5663
render() {
57-
if (this.state.hasError) {
64+
if (this.state.hasError && this.state.error && !this.state.error.message.includes("account changed")) {
5865
return this.props.fallback;
5966
}
6067
return this.props.children;
@@ -75,44 +82,93 @@ export default function RootLayout({
7582
const userAddress = useUserStore((state) => state.userAddress);
7683
const setUserAddress = useUserStore((state) => state.setUserAddress);
7784

85+
// Global error handler for unhandled promise rejections
86+
useEffect(() => {
87+
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
88+
console.error('Unhandled promise rejection:', event.reason);
89+
90+
// Handle wallet-related errors specifically
91+
if (event.reason && typeof event.reason === 'object') {
92+
const error = event.reason as Error;
93+
if (error.message && error.message.includes("account changed")) {
94+
console.log("Account changed error caught by global handler, reloading page...");
95+
event.preventDefault(); // Prevent the error from being logged to console
96+
window.location.reload();
97+
return;
98+
}
99+
}
100+
};
101+
102+
window.addEventListener('unhandledrejection', handleUnhandledRejection);
103+
104+
return () => {
105+
window.removeEventListener('unhandledrejection', handleUnhandledRejection);
106+
};
107+
}, []);
108+
78109
const { mutate: createUser } = api.user.createUser.useMutation({
79110
onError: (e) => console.error(e),
80111
});
112+
const { mutate: updateUser } = api.user.updateUser.useMutation({
113+
onError: (e) => console.error(e),
114+
});
81115

82116
// Single effect for address + user creation
83117
useEffect(() => {
84118
(async () => {
85119
if (!connected || !wallet) return;
86120

87-
// 1) Set user address in store
88-
let address = (await wallet.getUsedAddresses())[0];
89-
if (!address) address = (await wallet.getUnusedAddresses())[0];
90-
if (address) setUserAddress(address);
121+
try {
122+
// 1) Set user address in store
123+
let address = (await wallet.getUsedAddresses())[0];
124+
if (!address) address = (await wallet.getUnusedAddresses())[0];
125+
if (address) setUserAddress(address);
91126

92-
// 2) If user doesn't exist, create it
93-
if (!isLoading && user === null) {
127+
// 2) Get stake address
94128
const stakeAddress = (await wallet.getRewardAddresses())[0];
95129
if (!stakeAddress || !address) {
96130
console.error("No stake address or payment address found");
97131
return;
98132
}
99-
const nostrKey = generateNsec();
100-
createUser({
101-
address,
102-
stakeAddress,
103-
nostrKey: JSON.stringify(nostrKey),
104-
});
133+
134+
// 3) Get DRep key hash (optional)
135+
let drepKeyHash = "";
136+
try {
137+
const dRepKey = await wallet.getDRep();
138+
console.log("DRep key:", dRepKey);
139+
if (dRepKey && dRepKey.publicKeyHash) {
140+
drepKeyHash = dRepKey.publicKeyHash;
141+
}
142+
} catch (error) {
143+
// DRep key is optional, so we continue without it
144+
console.log("No DRep key available for this wallet");
145+
}
146+
147+
// 4) Create or update user (upsert pattern handles both cases)
148+
if (!isLoading) {
149+
const nostrKey = generateNsec();
150+
createUser({
151+
address,
152+
stakeAddress,
153+
drepKeyHash,
154+
nostrKey: JSON.stringify(nostrKey),
155+
});
156+
}
157+
} catch (error) {
158+
console.error("Error in wallet initialization effect:", error);
159+
160+
// If we get an "account changed" error, reload the page
161+
if (error instanceof Error && error.message.includes("account changed")) {
162+
console.log("Account changed detected, reloading page...");
163+
window.location.reload();
164+
return;
165+
}
166+
167+
// For other errors, don't throw to prevent app crash
168+
// The user can retry by reconnecting their wallet
105169
}
106170
})();
107-
}, [
108-
connected,
109-
wallet,
110-
user,
111-
isLoading,
112-
createUser,
113-
generateNsec,
114-
setUserAddress,
115-
]);
171+
}, [connected, wallet, user, isLoading, createUser, generateNsec, setUserAddress]);
116172

117173
const isWalletPath = router.pathname.includes("/wallets/[wallet]");
118174
const walletPageRoute = router.pathname.split("/wallets/[wallet]/")[1];
@@ -125,12 +181,18 @@ export default function RootLayout({
125181
{isLoading && <Loading />}
126182

127183
{/* Sidebar for larger screens */}
128-
<aside className="hidden border-r border-gray-200/30 dark:border-white/[0.03] bg-muted/40 md:block">
184+
<aside className="hidden border-r border-gray-200/30 bg-muted/40 dark:border-white/[0.03] md:block">
129185
<div className="flex h-full max-h-screen flex-col">
130-
<header className="flex h-14 items-center border-b border-gray-200/30 dark:border-white/[0.03] px-4 lg:h-16 lg:px-6" id="logo-header" data-header="sidebar">
186+
<header
187+
className="flex h-14 items-center border-b border-gray-200/30 px-4 dark:border-white/[0.03] lg:h-16 lg:px-6"
188+
id="logo-header"
189+
data-header="sidebar"
190+
>
131191
<Link href="/" className="flex items-center gap-3">
132192
<Logo />
133-
<span className="font-medium text-sm md:text-base lg:text-lg tracking-[-0.01em] select-none">Multi-Sig Platform</span>
193+
<span className="select-none text-sm font-medium tracking-[-0.01em] md:text-base lg:text-lg">
194+
Multi-Sig Platform
195+
</span>
134196
</Link>
135197
</header>
136198
<nav className="flex-1 pt-2">
@@ -143,27 +205,35 @@ export default function RootLayout({
143205

144206
{/* Main content area */}
145207
<div className="flex h-screen flex-col">
146-
<header className="pointer-events-auto relative z-10 border-b border-gray-200/30 dark:border-white/[0.03] bg-muted/40 px-4 lg:px-6" data-header="main">
208+
<header
209+
className="pointer-events-auto relative z-10 border-b border-gray-200/30 bg-muted/40 px-4 dark:border-white/[0.03] lg:px-6"
210+
data-header="main"
211+
>
147212
<div className="flex h-14 items-center gap-4 lg:h-16">
148213
{/* Mobile menu button */}
149214
<MobileNavigation isWalletPath={isWalletPath} />
150-
215+
151216
{/* Logo in mobile header - centered */}
152-
<div className="flex-1 flex justify-center md:hidden">
153-
<Link href="/" className="flex items-center gap-2 px-2 py-1 rounded-md hover:bg-gray-100/50 dark:hover:bg-gray-800/50 transition-colors">
217+
<div className="flex flex-1 justify-center md:hidden">
218+
<Link
219+
href="/"
220+
className="flex items-center gap-2 rounded-md px-2 py-1 transition-colors hover:bg-gray-100/50 dark:hover:bg-gray-800/50"
221+
>
154222
<svg
155-
className="h-7 w-7 text-foreground flex-shrink-0"
223+
className="h-7 w-7 flex-shrink-0 text-foreground"
156224
enableBackground="new 0 0 300 200"
157225
viewBox="0 0 300 200"
158226
xmlns="http://www.w3.org/2000/svg"
159227
fill="currentColor"
160228
>
161229
<path d="m289 127-45-60-45-60c-.9-1.3-2.4-2-4-2s-3.1.7-4 2l-37 49.3c-2 2.7-6 2.7-8 0l-37-49.3c-.9-1.3-2.4-2-4-2s-3.1.7-4 2l-45 60-45 60c-1.3 1.8-1.3 4.2 0 6l45 60c.9 1.3 2.4 2 4 2s3.1-.7 4-2l37-49.3c2-2.7 6-2.7 8 0l37 49.3c.9 1.3 2.4 2 4 2s3.1-.7 4-2l37-49.3c2-2.7 6-2.7 8 0l37 49.3c.9 1.3 2.4 2 4 2s3.1-.7 4-2l45-60c1.3-1.8 1.3-4.2 0-6zm-90-103.3 32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0l-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0zm-90 0 32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0l-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0zm-53 152.6-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0zm90 0-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0zm90 0-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0z" />
162230
</svg>
163-
<span className="text-base font-medium text-foreground whitespace-nowrap">Multi-Sig Platform</span>
231+
<span className="whitespace-nowrap text-base font-medium text-foreground">
232+
Multi-Sig Platform
233+
</span>
164234
</Link>
165235
</div>
166-
236+
167237
{/* Wallet selection + breadcrumb row on desktop */}
168238
{isLoggedIn && (
169239
<div className="hidden md:block">
@@ -210,7 +280,7 @@ export default function RootLayout({
210280
) : (
211281
<>
212282
{/* Desktop buttons */}
213-
<div className="hidden md:flex items-center space-x-2">
283+
<div className="hidden items-center space-x-2 md:flex">
214284
<WalletDataLoaderWrapper mode="button" />
215285
<DialogReportWrapper mode="button" />
216286
<UserDropDownWrapper mode="button" />
@@ -220,7 +290,7 @@ export default function RootLayout({
220290
<WalletDataLoaderWrapper mode="menu-item" />
221291
<DialogReportWrapper mode="menu-item" />
222292
<UserDropDownWrapper mode="menu-item" />
223-
<div className="h-px bg-border -mx-2 my-1" />
293+
<div className="-mx-2 my-1 h-px bg-border" />
224294
<LogoutWrapper mode="menu-item" />
225295
</MobileActionsMenu>
226296
</>
@@ -230,7 +300,22 @@ export default function RootLayout({
230300
</header>
231301

232302
<main className="relative flex flex-1 flex-col gap-4 overflow-y-auto overflow-x-hidden p-4 md:p-8">
233-
{pageIsPublic || userAddress ? children : <PageHomepage />}
303+
<WalletErrorBoundary
304+
fallback={
305+
<div className="flex flex-col items-center justify-center h-full">
306+
<h2 className="text-xl font-semibold mb-2">Something went wrong</h2>
307+
<p className="text-gray-600 mb-4">Please try refreshing the page or reconnecting your wallet.</p>
308+
<button
309+
onClick={() => window.location.reload()}
310+
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
311+
>
312+
Refresh Page
313+
</button>
314+
</div>
315+
}
316+
>
317+
{pageIsPublic || userAddress ? children : <PageHomepage />}
318+
</WalletErrorBoundary>
234319
</main>
235320
</div>
236321
</div>

0 commit comments

Comments
 (0)