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
7 changes: 5 additions & 2 deletions src/components/common/card-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ export default function CardUI({
cardClassName?: string;
headerDom?: ReactNode;
}) {
// Make title larger for wallet info card (col-span-2)
const isLargeTitle = cardClassName?.includes('col-span-2');

return (
<Card className={`w-full max-w-4xl ${cardClassName}`}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-xl font-medium">{title}</CardTitle>
<CardTitle className={isLargeTitle ? "text-2xl sm:text-3xl font-semibold" : "text-xl font-medium"}>{title}</CardTitle>
{headerDom && headerDom}
{icon && (
<>
Expand All @@ -33,7 +36,7 @@ export default function CardUI({
</>
)}
</CardHeader>
<CardContent className="overflow-y-auto max-h-[calc(100vh-200px)]">
<CardContent>
<div className="mt-1 flex flex-col gap-2">
{description && (
<p className="text-sm text-muted-foreground">{description}</p>
Expand Down
7 changes: 6 additions & 1 deletion src/components/common/discordIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
export default function DiscordIcon() {
interface DiscordIconProps {
className?: string;
}

export default function DiscordIcon({ className }: DiscordIconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16"
className={className}
>
<path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612" />
</svg>
Expand Down
26 changes: 21 additions & 5 deletions src/components/common/overall-layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -371,19 +371,35 @@ export default function RootLayout({
// Don't refetch here - let the natural query refetch handle it if needed
}, []);

const handleAuthModalAuthorized = useCallback(() => {
const handleAuthModalAuthorized = useCallback(async () => {
setShowAuthModal(false);
setCheckingSession(false);
setHasCheckedSession(true); // Mark as checked so we don't check again
// Show loading skeleton for smooth transition
setShowPostAuthLoading(true);
// Refetch session after authorization to update state (but don't show modal again)
void refetchWalletSession();

// Wait a moment for the cookie to be set by the browser, then refetch session
await new Promise(resolve => setTimeout(resolve, 200));

// Refetch session to update state
await refetchWalletSession();

// Invalidate wallet queries so they refetch with the new session
// Use a small delay to ensure cookie is available on subsequent requests
setTimeout(() => {
const userAddressForInvalidation = userAddress || address;
if (userAddressForInvalidation) {
void ctx.wallet.getUserWallets.invalidate({ address: userAddressForInvalidation });
void ctx.wallet.getUserNewWallets.invalidate({ address: userAddressForInvalidation });
void ctx.wallet.getUserNewWalletsNotOwner.invalidate({ address: userAddressForInvalidation });
}
}, 300);

// Hide loading after a brief delay to allow data to load
setTimeout(() => {
setShowPostAuthLoading(false);
}, 1000);
}, [refetchWalletSession]);
}, 1500);
}, [refetchWalletSession, ctx.wallet, userAddress, address]);

// Memoize computed route values
const isWalletPath = useMemo(() => router.pathname.includes("/wallets/[wallet]"), [router.pathname]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ export default function WalletDataLoaderWrapper({
}

function dRepIds() {
// Use multisig wallet DRep ID if available, otherwise fallback to appWallet
const dRepId = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getDRepId() : appWallet?.dRepId;
// Use multisig wallet DRep ID if available (it handles no DRep keys by using payment script),
// otherwise fallback to appWallet (for legacy wallets without multisigWallet)
const dRepId = multisigWallet ? multisigWallet.getDRepId() : appWallet?.dRepId;
if (!dRepId) return null;
return getDRepIds(dRepId);
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/multisig/proxy/ProxyOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { memo, useState, useEffect } from "react";
import { truncateTokenSymbol } from "@/utils/strings";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -270,7 +271,7 @@ const ProxyCard = memo(function ProxyCard({
<div className="text-sm space-y-1">
{displayBalance.map((asset: any, index: number) => (
<div key={index} className="flex justify-between">
<span>{asset.unit === "lovelace" ? "ADA" : asset.unit}:</span>
<span>{asset.unit === "lovelace" ? "ADA" : truncateTokenSymbol(asset.unit)}:</span>
<span className="font-mono">
{asset.unit === "lovelace"
? `${(parseFloat(asset.quantity) / 1000000).toFixed(6)} ADA`
Expand Down
3 changes: 2 additions & 1 deletion src/components/multisig/proxy/ProxySpend.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { memo } from "react";
import { truncateTokenSymbol } from "@/utils/strings";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
Expand Down Expand Up @@ -139,7 +140,7 @@ const ProxySpend = memo(function ProxySpend({
<div key={index} className="flex items-center justify-between p-3 border rounded-lg bg-muted/50">
<div className="flex items-center gap-2">
<Badge variant="secondary">
{asset.unit === "lovelace" ? "ADA" : asset.unit}
{asset.unit === "lovelace" ? "ADA" : truncateTokenSymbol(asset.unit)}
</Badge>
<span className="font-mono text-sm">
{asset.unit === "lovelace"
Expand Down
48 changes: 43 additions & 5 deletions src/components/pages/homepage/wallets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import { getFirstAndLast } from "@/utils/strings";
import { api } from "@/utils/api";
import { useUserStore } from "@/lib/zustand/user";
import { useSiteStore } from "@/lib/zustand/site";
import { buildMultisigWallet } from "@/utils/common";
import { buildMultisigWallet, getWalletType } from "@/utils/common";
import { addressToNetwork } from "@/utils/multisigSDK";

import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Archive } from "lucide-react";
import PageHeader from "@/components/common/page-header";
import CardUI from "@/components/common/card-content";
import RowLabelInfo from "@/components/common/row-label-info";
Expand All @@ -29,10 +31,21 @@ export default function PageWallets() {
const [showArchived, setShowArchived] = useState(false);
const userAddress = useUserStore((state) => state.userAddress);

// Check wallet session authorization before enabling queries
const { data: walletSession } = api.auth.getWalletSession.useQuery(
{ address: userAddress ?? "" },
{
enabled: !!userAddress && userAddress.length > 0,
refetchOnWindowFocus: false,
},
);
const isAuthorized = walletSession?.authorized ?? false;

const { data: newPendingWallets, isLoading: isLoadingNewWallets } = api.wallet.getUserNewWallets.useQuery(
{ address: userAddress! },
{
enabled: userAddress !== undefined,
// Only enable query when user is authorized (prevents 403 errors)
enabled: userAddress !== undefined && isAuthorized,
retry: (failureCount, error) => {
// Don't retry on authorization errors (403)
if (error && typeof error === "object") {
Expand Down Expand Up @@ -60,15 +73,24 @@ export default function PageWallets() {
api.wallet.getUserNewWalletsNotOwner.useQuery(
{ address: userAddress! },
{
enabled: userAddress !== undefined,
// Only enable query when user is authorized (prevents 403 errors)
enabled: userAddress !== undefined && isAuthorized,
retry: (failureCount, error) => {
// Don't retry on authorization errors (403)
if (error && typeof error === "object") {
const err = error as { code?: string; message?: string; data?: { code?: string } };
const err = error as {
code?: string;
message?: string;
data?: { code?: string; httpStatus?: number };
shape?: { code?: string; message?: string };
};
const errorMessage = err.message || err.shape?.message || "";
const isAuthError =
err.code === "FORBIDDEN" ||
err.data?.code === "FORBIDDEN" ||
err.message?.includes("Address mismatch");
err.data?.httpStatus === 403 ||
err.shape?.code === "FORBIDDEN" ||
errorMessage.includes("Address mismatch");
if (isAuthError) return false;
}
return failureCount < 1;
Expand Down Expand Up @@ -216,6 +238,11 @@ function CardWallet({
walletId: wallet.id,
});

// Check wallet type for badge display using centralized detection
const walletType = getWalletType(wallet);
const isSummonWallet = walletType === 'summon';
const isLegacyWallet = walletType === 'legacy';

// Rebuild the multisig wallet to get the correct canonical address for display
// This ensures we show the correct address even if wallet.address was built incorrectly
const displayAddress = useMemo(() => {
Expand All @@ -240,6 +267,17 @@ function CardWallet({
title={`${wallet.name}${wallet.isArchived ? " (Archived)" : ""}`}
description={wallet.description}
cardClassName=""
headerDom={
isSummonWallet ? (
<Badge
variant="outline"
className="text-xs bg-orange-600/10 border-orange-600/30 text-orange-700 dark:text-orange-400"
>
<Archive className="h-3 w-3 mr-1" />
Summon
</Badge>
) : undefined
}
>
<WalletBalance balance={balance} loadingState={loadingState} />
<RowLabelInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { api } from "@/utils/api";
import { useToast } from "@/hooks/use-toast";
import { useUserStore } from "@/lib/zustand/user";
import { useSiteStore } from "@/lib/zustand/site";
import { buildWallet } from "@/hooks/common";
import { buildWallet } from "@/utils/common";

import PageHeader from "@/components/common/page-header";
import {
Expand Down
13 changes: 9 additions & 4 deletions src/components/pages/wallet/governance/ballot/ballot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ export default function BallotCard({
const proxy = proxies.find((p: any) => p.id === selectedProxyId);
if (!proxy) throw new Error("Proxy not found");

if (!multisigWallet) throw new Error("Multisig Wallet could not be built.");
const meshTxBuilder = getTxBuilder(network);
const proxyContract = new MeshProxyContract(
{
Expand Down Expand Up @@ -314,7 +313,12 @@ export default function BallotCard({
});

// Vote using proxy
const txBuilder = await proxyContract.voteProxyDrep(votes, utxos, multisigWallet?.getScript().address);
// Use multisig wallet address if available, otherwise fallback to appWallet (for legacy wallets)
const proxyAddress = multisigWallet?.getScript().address || appWallet?.address;
if (!proxyAddress) {
throw new Error("Wallet address not found");
}
const txBuilder = await proxyContract.voteProxyDrep(votes, utxos, proxyAddress);

await newTransaction({
txBuilder: txBuilder,
Expand Down Expand Up @@ -401,8 +405,9 @@ export default function BallotCard({

setLoading(true);
try {
if (!multisigWallet) throw new Error("Multisig Wallet could not be built.");
const dRepId = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getDRepId() : appWallet?.dRepId;
// Use multisig wallet DRep ID if available (it handles no DRep keys by using payment script),
// otherwise fallback to appWallet (for legacy wallets without multisigWallet)
const dRepId = multisigWallet ? multisigWallet.getDRepId() : appWallet?.dRepId;
if (!dRepId) {
setAlert("DRep not found");
toast({
Expand Down
4 changes: 3 additions & 1 deletion src/components/pages/wallet/governance/card-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export default function CardInfo({ appWallet, manualUtxos }: { appWallet: Wallet
} | null>(null);

// Get DRep info for standard mode
const currentDrepId = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getDRepId() : appWallet?.dRepId;
// Use multisig wallet DRep ID if available (it handles no DRep keys by using payment script),
// otherwise fallback to appWallet (for legacy wallets without multisigWallet)
const currentDrepId = multisigWallet ? multisigWallet.getDRepId() : appWallet?.dRepId;
const currentDrepInfo = drepInfo;


Expand Down
46 changes: 36 additions & 10 deletions src/components/pages/wallet/governance/drep/registerDrep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,47 @@ export default function RegisterDRep({ onClose }: RegisterDRepProps = {}) {
}

async function registerDrep(): Promise<void> {
if (!connected || !userAddress || !multisigWallet || !appWallet)
throw new Error("Multisig wallet not connected");
if (!connected || !userAddress || !appWallet)
throw new Error("Wallet not connected");

setLoading(true);
const txBuilder = getTxBuilder(network);

const drepData = multisigWallet?.getDRep(appWallet);
if (!drepData) {
throw new Error("DRep not found");
// For legacy wallets (no multisigWallet), use appWallet values directly (preserves input order)
// For SDK wallets, use multisigWallet to compute DRep ID and script
let dRepId: string;
let drepCbor: string;
let scriptCbor: string;
let changeAddress: string;

if (multisigWallet) {
const drepData = multisigWallet.getDRep(appWallet);
if (!drepData) {
throw new Error("DRep not found");
}
dRepId = drepData.dRepId;
drepCbor = drepData.drepCbor;
const multisigScript = multisigWallet.getScript();
const multisigScriptCbor = multisigScript.scriptCbor;
const appScriptCbor = appWallet.scriptCbor;
if (!multisigScriptCbor && !appScriptCbor) {
throw new Error("Script CBOR not found");
}
scriptCbor = multisigWallet.getKeysByRole(3) ? (multisigScriptCbor || appScriptCbor!) : (appScriptCbor || multisigScriptCbor!);
changeAddress = multisigScript.address;
} else {
// Legacy wallet: use appWallet values (computed with input order preserved)
if (!appWallet.dRepId || !appWallet.scriptCbor) {
throw new Error("DRep ID or script not found for legacy wallet");
}
dRepId = appWallet.dRepId;
drepCbor = appWallet.scriptCbor; // Use payment script CBOR for legacy wallets
scriptCbor = appWallet.scriptCbor;
changeAddress = appWallet.address;
}
const { dRepId, drepCbor } = drepData;

const scriptCbor = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getScript().scriptCbor : appWallet.scriptCbor;
if (!scriptCbor) {
throw new Error("Script not found");
if (!scriptCbor || !changeAddress) {
throw new Error("Script or change address not found");
}
try {
const { anchorUrl, anchorHash } = await createAnchor();
Expand Down Expand Up @@ -157,7 +183,7 @@ export default function RegisterDRep({ onClose }: RegisterDRepProps = {}) {
anchorDataHash: anchorHash,
})
.certificateScript(drepCbor)
.changeAddress(multisigWallet.getScript().address);
.changeAddress(changeAddress);



Expand Down
13 changes: 9 additions & 4 deletions src/components/pages/wallet/governance/proposal/voteButtton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ export default function VoteButton({
};

// Vote using proxy
const txBuilderResult = await proxyContract.voteProxyDrep([voteData], utxos, multisigWallet?.getScript().address);
// Use multisig wallet address if available, otherwise fallback to appWallet (for legacy wallets)
const proxyAddress = multisigWallet?.getScript().address || appWallet?.address;
if (!proxyAddress) {
throw new Error("Wallet address not found");
}
const txBuilderResult = await proxyContract.voteProxyDrep([voteData], utxos, proxyAddress);

await newTransaction({
txBuilder: txBuilderResult,
Expand Down Expand Up @@ -229,9 +234,9 @@ export default function VoteButton({
setLoading(false);
return;
}
if (!multisigWallet)
throw new Error("Multisig Wallet could not be built.");
const dRepId = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getDRepId() : appWallet?.dRepId;
// Use multisig wallet DRep ID if available (it handles no DRep keys by using payment script),
// otherwise fallback to appWallet (for legacy wallets without multisigWallet)
const dRepId = multisigWallet ? multisigWallet.getDRepId() : appWallet?.dRepId;
if (!dRepId) {
setAlert("DRep not found");
toast({
Expand Down
4 changes: 3 additions & 1 deletion src/components/pages/wallet/governance/proposals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ export default function AllProposals({ appWallet, utxos, selectedBallotId, onSel
const order = "desc";

// Get DRep ID for fetching voting history (proxy mode or standard mode)
const standardDrepId = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getDRepId() : appWallet?.dRepId;
// Use multisig wallet DRep ID if available (it handles no DRep keys by using payment script),
// otherwise fallback to appWallet (for legacy wallets without multisigWallet)
const standardDrepId = multisigWallet ? multisigWallet.getDRepId() : appWallet?.dRepId;

// Get proxy DRep ID if proxy is enabled
useEffect(() => {
Expand Down
Loading
Loading