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: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ docker-compose*.yml
tsconfig.tsbuildinfo




61 changes: 43 additions & 18 deletions src/components/pages/wallet/governance/drep/updateDrep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ export default function UpdateDRep({ onClose }: UpdateDRepProps = {}) {
if (!appWallet) {
throw new Error("Wallet not connected");
}
if (!multisigWallet) {
throw new Error("Multisig wallet not connected");
}
// Note: multisigWallet can be undefined for legacy wallets, which is handled in updateDrep()
// Get metadata with both compacted (for upload) and normalized (for hashing) forms
const metadataResult = await getDRepMetadata(
formState,
Expand Down Expand Up @@ -113,8 +111,13 @@ export default function UpdateDRep({ onClose }: UpdateDRepProps = {}) {
}

async function updateProxyDrep(): Promise<void> {
if (!connected || !userAddress || !multisigWallet || !appWallet) {
throw new Error("Multisig wallet not connected");
if (!connected || !userAddress || !appWallet) {
throw new Error("Wallet not connected");
}
// Proxy mode requires multisigWallet (SDK wallets only)
if (!multisigWallet) {
// Fall back to standard update for legacy wallets
return updateDrep();
}
if (!hasValidProxy) {
// Fall back to standard update if no valid proxy
Expand Down Expand Up @@ -175,25 +178,47 @@ export default function UpdateDRep({ onClose }: UpdateDRepProps = {}) {
}

async function updateDrep(): 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");
}
const { dRepId, drepCbor } = drepData;
// 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;

const scriptCbor = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getScript().scriptCbor : appWallet.scriptCbor;
if (!scriptCbor) {
throw new Error("Script not found");
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 changeAddress = multisigWallet?.getKeysByRole(3) ? multisigWallet?.getScript().address : appWallet.address;
if (!changeAddress) {
throw new Error("Change address not found");

if (!scriptCbor || !changeAddress) {
throw new Error("Script or change address not found");
}
try {
const { anchorUrl, anchorHash } = await createAnchor();
Expand Down
21 changes: 13 additions & 8 deletions src/components/pages/wallet/new-transaction/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -530,16 +530,21 @@ export default function PageNewTransaction({ onSuccess }: { onSuccess?: () => vo
return null;
}

// Check if component is used in a modal (has onSuccess prop)
const isInModal = !!onSuccess;

return (
<div className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-3 sm:gap-4 md:gap-6">
<div className={`mx-auto flex w-full max-w-6xl flex-1 flex-col gap-3 sm:gap-4 md:gap-6 ${isInModal ? 'pt-0' : ''}`}>
{/* Hide title/description when used in modal (they're in DialogHeader) */}
<div className="hidden flex-col gap-2 sm:flex">
<SectionTitle>New Transaction</SectionTitle>
<p className="text-sm leading-relaxed text-muted-foreground">
Create a new multisig transaction by specifying recipients, amounts,
and transaction details.
</p>
</div>
{!isInModal && (
<div className="flex flex-col gap-2">
<SectionTitle>New Transaction</SectionTitle>
<p className="text-sm leading-relaxed text-muted-foreground">
Create a new multisig transaction by specifying recipients, amounts,
and transaction details.
</p>
</div>
)}

<div className="grid gap-3 sm:gap-4 md:gap-6">
<CardUI title="Description" cardClassName="w-full">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export default function NewTransactionDialog({

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="h-[calc(100dvh-3.5rem)] max-h-[calc(100dvh-3.5rem)] w-[100vw] max-w-[100vw] top-[3.5rem] translate-x-[-50%] translate-y-0 rounded-none border-0 border-t p-0 sm:top-[50%] sm:translate-y-[-50%] sm:h-[95vh] sm:max-h-[95vh] sm:w-[95vw] sm:max-w-[95vw] sm:rounded-lg sm:border sm:p-6 md:max-w-[90vw] lg:max-w-[85vw] lg:top-[50%]">
<DialogContent className="h-[calc(100dvh-3.5rem)] max-h-[calc(100dvh-3.5rem)] w-[100vw] max-w-[100vw] top-[3.5rem] left-0 right-0 translate-x-0 translate-y-0 rounded-none border-0 border-t p-0 sm:top-[4rem] sm:left-[50%] sm:right-auto sm:translate-x-[-50%] sm:h-[calc(100vh-4rem)] sm:max-h-[calc(100vh-4rem)] sm:w-[95vw] sm:max-w-[95vw] sm:rounded-lg sm:border sm:p-0 md:max-w-[90vw] lg:max-w-[85vw]">
<div className="flex h-full flex-col overflow-hidden">
{/* Sticky Header */}
<DialogHeader className="flex-shrink-0 border-b px-4 py-3 sm:px-0 sm:py-0 sm:border-b-0">
<DialogHeader className="flex-shrink-0 border-b bg-background px-4 py-3 sm:px-6 sm:py-4">
<DialogTitle className="text-lg sm:text-xl">New Transaction</DialogTitle>
<DialogDescription className="hidden text-sm sm:block">
Create a new multisig transaction by specifying recipients, amounts,
Expand All @@ -42,7 +42,7 @@ export default function NewTransactionDialog({
</DialogHeader>

{/* Scrollable Content */}
<div className="flex-1 overflow-y-auto overscroll-contain px-3 py-3 sm:px-0 sm:py-4">
<div className="flex-1 overflow-y-auto overscroll-contain px-3 py-3 sm:px-6 sm:py-4">
<PageNewTransaction onSuccess={handleSuccess} />
</div>
</div>
Expand Down
27 changes: 19 additions & 8 deletions src/components/pages/wallet/transactions/transaction-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -379,26 +379,33 @@ export default function TransactionCard({
);
})}
</div>
<div className="flex items-center gap-2 mt-1">
<span className="text-xs text-muted-foreground font-mono">
<div className="flex items-center gap-2 mt-1 flex-wrap min-w-0">
<span className="text-xs text-muted-foreground font-mono truncate min-w-0 flex-1">
to {output.address.length > 20
? `${output.address.slice(0, 10)}...${output.address.slice(-10)}`
: output.address}
</span>
{(() => {
const addressLabel = getAddressLabel(output.address);
if (addressLabel.label) {
// Use shorter label on mobile for "Self (Multisig)"
const displayLabel = addressLabel.type === "self" && addressLabel.label === "Self (Multisig)"
? "Self"
: addressLabel.label;

return (
<Badge
variant="outline"
className={cn(
"text-xs shrink-0",
"text-xs shrink-0 max-w-full",
addressLabel.type === "self" && "border-blue-500 text-blue-700 dark:text-blue-400",
addressLabel.type === "signer" && "border-green-500 text-green-700 dark:text-green-400",
addressLabel.type === "contact" && "border-purple-500 text-purple-700 dark:text-purple-400"
)}
title={addressLabel.label !== displayLabel ? addressLabel.label : undefined}
>
{addressLabel.label}
<span className="hidden sm:inline">{addressLabel.label}</span>
<span className="sm:hidden">{displayLabel}</span>
</Badge>
);
}
Expand Down Expand Up @@ -931,12 +938,12 @@ export default function TransactionCard({
{userAddress &&
!transaction.signedAddresses.includes(userAddress) &&
!transaction.rejectedAddresses.includes(userAddress) && (
<CardFooter className="flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-2 border-t bg-muted/50 px-4 sm:px-6 py-3">
<CardFooter className="flex items-center gap-2 border-t bg-muted/50 px-4 sm:px-6 py-3">
<Button
onClick={() => signTx()}
disabled={loading}
loading={loading}
className={`w-full sm:w-auto ${loading ? 'flex-1' : 'flex-1 sm:flex-none'} relative overflow-hidden transition-all duration-300 ${
className={`flex-1 h-10 relative overflow-hidden transition-all duration-300 ${
loading
? "bg-primary/90 shadow-lg shadow-primary/20 cursor-wait"
: ""
Expand All @@ -956,9 +963,13 @@ export default function TransactionCard({
<Button
variant="destructive"
onClick={() => rejectTx()}
className="w-full sm:w-auto flex-1"
className="h-10 w-10 flex-shrink-0 sm:hover:min-w-[90px] sm:hover:w-auto px-0 sm:hover:px-3 transition-all duration-300 ease-in-out flex items-center justify-center gap-2 group overflow-hidden"
title="Reject"
>
Reject
<X className="h-4 w-4 flex-shrink-0" />
<span className="hidden sm:inline-block max-w-0 sm:group-hover:max-w-[60px] opacity-0 sm:group-hover:opacity-100 transition-all duration-300 whitespace-nowrap overflow-hidden">
Reject
</span>
</Button>
)}
</CardFooter>
Expand Down
Loading