Transaction Signing

After building a Soroban transaction via the backend API, the frontend signs it with the user's wallet.

Deposit Transaction Flow

typescript
import * as StellarSdk from "@stellar/stellar-sdk";

async function executeDeposit(vaultId: string, amount: number) {
    const wallet = getConnectedWallet(); // from wallet provider

    // 1. Get transaction envelope from backend
    const res = await fetch("/api/v1/deposit", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            vault_id: vaultId,
            amount,
            wallet_address: wallet.address,
        }),
    });
    const { data } = await res.json();

    // 2. Sign with wallet extension
    const { signedTxXdr } = await kit.selectedModule.signTransaction(
        data.transaction_xdr,
        {
            networkPassphrase: StellarSdk.Networks.TESTNET,
        }
    );

    // 3. Submit to Stellar network
    const server = new StellarSdk.SorobanRpc.Server(
        "https://soroban-testnet.stellar.org"
    );
    const tx = StellarSdk.TransactionBuilder.fromXDR(
        signedTxXdr,
        StellarSdk.Networks.TESTNET
    );
    const result = await server.sendTransaction(tx);

    // 4. Poll for confirmation
    if (result.status === "PENDING") {
        const confirmed = await pollTransaction(server, result.hash);
        return confirmed;
    }

    return result;
}

Error Handling

typescript
// StellarWalletsKit throws IKitError objects, not Error instances
interface IKitError {
    code: number;
    message: string;
}

function extractErrorMessage(err: unknown): string {
    if (err && typeof err === "object") {
        if ("message" in err) return String((err as IKitError).message);
    }
    if (err instanceof Error) return err.message;
    return "Unknown error";
}