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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"db:studio": "prisma studio",
"db:update": "prisma format && prisma db push && prisma generate",
"dev": "next dev",
"postinstall": "prisma format && prisma generate",
"postinstall": "prisma generate",
"lint": "next lint",
"start": "next start",
"test": "jest",
Expand Down
93 changes: 79 additions & 14 deletions src/components/common/modals/WalletAuthModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect, useCallback } from "react";
import { useWallet } from "@meshsdk/react";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
Expand All @@ -9,14 +9,16 @@ interface WalletAuthModalProps {
open: boolean;
onClose: () => void;
onAuthorized?: () => void;
autoAuthorize?: boolean; // If true, automatically trigger authorization when modal opens
}

export function WalletAuthModal({ address, open, onClose, onAuthorized }: WalletAuthModalProps) {
export function WalletAuthModal({ address, open, onClose, onAuthorized, autoAuthorize = false }: WalletAuthModalProps) {
const { wallet, connected } = useWallet();
const { toast } = useToast();
const [submitting, setSubmitting] = useState(false);
const [hasAutoAuthorized, setHasAutoAuthorized] = useState(false);

const handleAuthorize = async () => {
const handleAuthorize = useCallback(async () => {
if (!wallet || !connected) {
toast({
title: "No wallet connected",
Expand All @@ -27,11 +29,33 @@ export function WalletAuthModal({ address, open, onClose, onAuthorized }: Wallet
}
setSubmitting(true);
try {
// Resolve the payment address the wallet uses (match Swagger flow)
const usedAddresses = await wallet.getUsedAddresses();
const signingAddress = usedAddresses[0];
// Resolve the payment address the wallet uses
// Try used addresses first, fall back to unused addresses if needed
let signingAddress: string | undefined;
try {
const usedAddresses = await wallet.getUsedAddresses();
signingAddress = usedAddresses[0];
} catch (error) {
if (error instanceof Error && error.message.includes("account changed")) {
throw error;
}
// If getUsedAddresses fails for other reasons, try unused addresses
}

// Fall back to unused addresses if no used addresses found
if (!signingAddress) {
try {
const unusedAddresses = await wallet.getUnusedAddresses();
signingAddress = unusedAddresses[0];
} catch (error) {
if (error instanceof Error && error.message.includes("account changed")) {
throw error;
}
}
}

if (!signingAddress) {
throw new Error("No used addresses found for wallet");
throw new Error("No addresses found for wallet");
}

// 1) Get nonce from existing endpoint
Expand Down Expand Up @@ -100,7 +124,6 @@ export function WalletAuthModal({ address, open, onClose, onAuthorized }: Wallet
onAuthorized?.();
onClose();
} catch (error: any) {
console.error("WalletAuthModal authorize error:", error);
toast({
title: "Authorization failed",
description: error?.message || "Unable to authorize wallet. Please try again.",
Expand All @@ -109,16 +132,56 @@ export function WalletAuthModal({ address, open, onClose, onAuthorized }: Wallet
} finally {
setSubmitting(false);
}
};
}, [wallet, connected, toast, onAuthorized, onClose]);

// Auto-authorize when modal opens if autoAuthorize is true (only once)
useEffect(() => {
if (open && autoAuthorize && !hasAutoAuthorized && wallet && connected && !submitting) {
// Small delay to ensure modal is fully rendered before triggering wallet prompt
const timeoutId = setTimeout(() => {
setHasAutoAuthorized(true);
void handleAuthorize();
}, 100);

return () => clearTimeout(timeoutId);
}
}, [open, autoAuthorize, hasAutoAuthorized, wallet, connected, submitting, handleAuthorize]);

// Reset auto-authorize flag when modal closes
useEffect(() => {
if (!open) {
setHasAutoAuthorized(false);
}
}, [open]);

return (
<Dialog open={open} onOpenChange={(open) => !open && !submitting && onClose()}>
<DialogContent>
<Dialog open={open} onOpenChange={(open) => {
// Prevent closing during authorization
if (!open && !submitting) {
onClose();
}
}}>
<DialogContent onPointerDownOutside={(e) => {
// Prevent closing by clicking outside during authorization
if (submitting) {
e.preventDefault();
}
}} onEscapeKeyDown={(e) => {
// Prevent closing with Escape key during authorization
if (submitting) {
e.preventDefault();
}
}}>
<DialogHeader>
<DialogTitle>Authorize this wallet</DialogTitle>
<DialogDescription>
To use this wallet with multisig, we need to confirm you control it by signing a
short message. This does not move any funds or create a transaction.
{autoAuthorize && !hasAutoAuthorized && (
<span className="block mt-2 text-sm font-medium">
Please approve the signing request in your wallet.
</span>
)}
</DialogDescription>
</DialogHeader>
<div className="mt-4 space-y-2 text-sm text-muted-foreground break-all">
Expand All @@ -127,9 +190,11 @@ export function WalletAuthModal({ address, open, onClose, onAuthorized }: Wallet
</div>
</div>
<div className="mt-6 flex justify-end gap-2">
<Button variant="outline" onClick={onClose} disabled={submitting}>
Cancel
</Button>
{!autoAuthorize && (
<Button variant="outline" onClick={onClose} disabled={submitting}>
Cancel
</Button>
)}
<Button onClick={handleAuthorize} disabled={submitting}>
{submitting ? "Authorizing..." : "Authorize"}
</Button>
Expand Down
Loading
Loading