diff --git a/src/components/TransactionBuilder/SignStep.tsx b/src/components/TransactionBuilder/SignStep.tsx
index 4dc8252..b75e8fe 100644
--- a/src/components/TransactionBuilder/SignStep.tsx
+++ b/src/components/TransactionBuilder/SignStep.tsx
@@ -1,10 +1,17 @@
-import React, { useState } from "react";
+import React, { useState, useEffect } from "react";
import { clsx } from "clsx";
import { isValidSecretKey } from "../../lib/stellar";
+import {
+ isFreighterInstalled,
+ signWithFreighter,
+ isAlbedoAvailable,
+ signWithAlbedo,
+} from "../../wallets";
interface SignStepProps {
xdr: string | null;
signedXdr: string | null;
+ networkPassphrase?: string;
onSign: (secretKey: string) => void;
onSignedXdrChange: (xdr: string) => void;
onNext: () => void;
@@ -15,17 +22,54 @@ interface SignStepProps {
export function SignStep({
xdr,
signedXdr,
+ networkPassphrase = "Test SDF Network ; September 2015",
onSign,
onSignedXdrChange,
onNext,
onBack,
showSubmit,
}: SignStepProps) {
- const [secretKey, setSecretKey] = useState("");
+const [secretKey, setSecretKey] = useState("");
const [signingMode, setSigningMode] = useState<"manual" | "paste">("manual");
const [showKey, setShowKey] = useState(false);
const [signError, setSignError] = useState(null);
const [copied, setCopied] = useState(false);
+ const [walletSigning, setWalletSigning] = useState(false);
+ const [freighterAvailable, setFreighterAvailable] = useState(false);
+ const [albedoAvailable, setAlbedoAvailable] = useState(false);
+
+ useEffect(() => {
+ setFreighterAvailable(isFreighterInstalled());
+ setAlbedoAvailable(isAlbedoAvailable());
+ }, []);
+
+ const handleFreighterSign = async () => {
+ if (!xdr) return;
+ setWalletSigning(true);
+ setSignError(null);
+ try {
+ const signed = await signWithFreighter(xdr, networkPassphrase);
+ onSignedXdrChange(signed);
+ } catch (e) {
+ setSignError((e as Error).message);
+ } finally {
+ setWalletSigning(false);
+ }
+ };
+
+ const handleAlbedoSign = async () => {
+ if (!xdr) return;
+ setWalletSigning(true);
+ setSignError(null);
+ try {
+ const signed = await signWithAlbedo(xdr, networkPassphrase);
+ onSignedXdrChange(signed);
+ } catch (e) {
+ setSignError((e as Error).message);
+ } finally {
+ setWalletSigning(false);
+ }
+ };
const isKeyValid = isValidSecretKey(secretKey);
@@ -54,6 +98,51 @@ export function SignStep({
+ {/* Wallet signing */}
+ {(freighterAvailable || albedoAvailable) && (
+
+
Sign with wallet
+
+ {freighterAvailable && (
+
+ )}
+ {albedoAvailable && (
+
+ )}
+
+
+
Or sign manually below
+
+
+ )}
+
+ {!freighterAvailable && !albedoAvailable && (
+
+
+ 💡 No wallet extension detected. Install{" "}
+ Freighter
+ {" "}or use{" "}
+ Albedo
+ {" "}to sign without exposing your secret key.
+
+
+ )}
+
+
{/* Signing mode */}
{(["manual", "paste"] as const).map((mode) => (
diff --git a/src/wallets/albedo.ts b/src/wallets/albedo.ts
new file mode 100644
index 0000000..81e39c6
--- /dev/null
+++ b/src/wallets/albedo.ts
@@ -0,0 +1,23 @@
+// Albedo wallet adapter
+export function isAlbedoAvailable(): boolean {
+ return typeof window !== 'undefined' && !!(window as any).albedo;
+}
+
+export async function signWithAlbedo(
+ xdr: string,
+ networkPassphrase: string
+): Promise {
+ if (!isAlbedoAvailable()) throw new Error('Albedo is not available');
+ const result = await (window as any).albedo.tx({
+ xdr,
+ network: networkPassphrase.includes('Public') ? 'public' : 'testnet',
+ submit: false,
+ });
+ return result.signed_envelope_xdr;
+}
+
+export async function getAlbedoPublicKey(): Promise {
+ if (!isAlbedoAvailable()) throw new Error('Albedo is not available');
+ const result = await (window as any).albedo.publicKey({});
+ return result.pubkey;
+}
diff --git a/src/wallets/freighter.ts b/src/wallets/freighter.ts
new file mode 100644
index 0000000..59b915d
--- /dev/null
+++ b/src/wallets/freighter.ts
@@ -0,0 +1,27 @@
+// Freighter wallet adapter
+export interface FreighterAdapter {
+ isInstalled: () => boolean;
+ getPublicKey: () => Promise;
+ signTransaction: (xdr: string, network: string) => Promise;
+}
+
+export function isFreighterInstalled(): boolean {
+ return typeof window !== 'undefined' && !!(window as any).freighter;
+}
+
+export async function getFreighterPublicKey(): Promise {
+ if (!isFreighterInstalled()) throw new Error('Freighter is not installed');
+ return await (window as any).freighterApi.getPublicKey();
+}
+
+export async function signWithFreighter(
+ xdr: string,
+ networkPassphrase: string
+): Promise {
+ if (!isFreighterInstalled()) throw new Error('Freighter is not installed');
+ const result = await (window as any).freighterApi.signTransaction(xdr, {
+ networkPassphrase,
+ });
+ if (result.error) throw new Error(result.error);
+ return result.signedTxXdr;
+}
diff --git a/src/wallets/index.ts b/src/wallets/index.ts
new file mode 100644
index 0000000..ae91759
--- /dev/null
+++ b/src/wallets/index.ts
@@ -0,0 +1,13 @@
+export * from './freighter';
+export * from './albedo';
+
+export type WalletType = 'freighter' | 'albedo' | 'secretKey' | 'none';
+
+export function detectAvailableWallets(): WalletType[] {
+ const wallets: WalletType[] = ['secretKey'];
+ if (typeof window !== 'undefined') {
+ if ((window as any).freighter) wallets.unshift('freighter');
+ if ((window as any).albedo) wallets.unshift('albedo');
+ }
+ return wallets;
+}