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
13 changes: 13 additions & 0 deletions demos/freelance-escrow/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,16 @@
background: var(--bg);
color: var(--text);
}

.wallet-button-container {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.5rem;
}

.wallet-error {
margin: 0;
font-size: 0.85rem;
color: #ef4444;
}
11 changes: 7 additions & 4 deletions demos/freelance-escrow/src/components/WalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useWallet } from "../hooks/useWallet";
import { shortenAddress } from "../lib/wallet";

export function WalletButton() {
const { wallet, connecting, connect } = useWallet();
const { wallet, connecting, error, connect } = useWallet();

if (wallet.status === "unavailable") {
return (
Expand All @@ -27,8 +27,11 @@ export function WalletButton() {
}

return (
<button className="wallet-button" onClick={connect} disabled={connecting}>
{connecting ? "Connecting..." : "Connect wallet"}
</button>
<div className="wallet-button-container">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wallet-button-container and wallet-error (line 34) aren't defined anywhere in App.css or index.css, so once error is actually set, it renders with default browser styling and undefined layout relative to the button.

<button className="wallet-button" onClick={connect} disabled={connecting}>
{connecting ? "Connecting..." : "Connect wallet"}
</button>
{error && <p className="wallet-error">{error}</p>}
</div>
);
}
15 changes: 12 additions & 3 deletions demos/freelance-escrow/src/hooks/useWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@ import { type WalletState, connectWallet, detectWallet } from "../lib/wallet";
export function useWallet() {
const [wallet, setWallet] = useState<WalletState>({ status: "disconnected" });
const [connecting, setConnecting] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detectWallet() is called here with no .catch(), but resolveConnectedState() (which detectWallet() calls internally) now throws instead of returning a disconnected state, per this PR's fix in lib/wallet.ts. A user with a wallet connected and allowed, but whose address retrieval fails for any reason, previously saw this resolve gracefully to { status: "disconnected" }. Now it throws, and since there's no catch here, that becomes an unhandled promise rejection on page load instead. Please add a .catch() here too, same pattern as connect()'s try/catch, or have detectWallet() itself catch and fall back to disconnected internally.

detectWallet().then(setWallet);
detectWallet()
.then(setWallet)
.catch(() => {
setWallet({ status: "disconnected" });
});
}, []);

const connect = useCallback(async () => {
setConnecting(true);
setError(null);
try {
setWallet(await connectWallet());
const state = await connectWallet();
setWallet(state);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch never fires for the case it's meant to handle. connectWallet() in lib/wallet.ts already returns { status: "disconnected" } when requestAccess() reports access.error (the user rejects the Freighter popup), it never throws for that. So a real rejection just resolves normally, error stays null, and the UI reverts to the plain button with no explanation, the exact silent failure #94 describes. The fix needs to happen in connectWallet()/resolveConnectedState() itself: surface the error result instead of collapsing it to disconnected, or have this hook check wallet.status after the await rather than relying on a throw that never happens.

} catch (err) {
setError(err instanceof Error ? err.message : "Failed to connect wallet.");
} finally {
setConnecting(false);
}
}, []);

return { wallet, connecting, connect };
return { wallet, connecting, error, connect };
}
16 changes: 13 additions & 3 deletions demos/freelance-escrow/src/lib/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ export async function detectWallet(): Promise<WalletState> {
return { status: "disconnected" };
}

return resolveConnectedState();
try {
return await resolveConnectedState();
} catch {
return { status: "disconnected" };
}
}

export async function connectWallet(): Promise<WalletState> {
const access = await requestAccess();
if (access.error) {
return { status: "disconnected" };
throw new Error(
typeof access.error === "string" ? access.error : "Failed to connect wallet"
);
}
return resolveConnectedState();
}
Expand All @@ -41,7 +47,11 @@ async function resolveConnectedState(): Promise<WalletState> {
]);

if (addressResult.error || !addressResult.address) {
return { status: "disconnected" };
throw new Error(
typeof addressResult.error === "string"
? addressResult.error
: "Failed to retrieve wallet address"
);
}

return {
Expand Down
Loading