> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# signTransaction

> Signs an SVM transaction using the specified wallet

## Function Signature

```typescript theme={"system"}
signTransaction(params: {
  walletMetadata: WalletMetadata;
  transaction: VersionedTransaction | Transaction | string;
  password?: string;
  externalServerKeyShares?: ServerKeyShare[];
  sponsor?: boolean;
}): Promise<string>
```

## Description

Signs an SVM transaction using the wallet identified by the supplied `walletMetadata`. The SDK is stateless — every call requires `walletMetadata`. Accepts a `Transaction`, `VersionedTransaction`, or hex string. Optionally requests gas sponsorship from Dynamic before signing.

> [!WARNING]
>
> When you pass `externalServerKeyShares` (the caller-supplied path), `walletMetadata.externalServerKeySharesBackupInfo` must also be present — `signTransaction` throws if shares are supplied but backup metadata is missing. The full `walletMetadata` returned from `createWalletAccount` / `importPrivateKey` already includes it; identity-only metadata from `fetchWalletMetadata` will be rejected.

## Parameters

### Required Parameters

* **`walletMetadata`** ([`WalletMetadata`](https://docs.dynamic.xyz/docs/node/reference/types/wallet-metadata)): Non-sensitive wallet metadata persisted from `createWalletAccount()` / `importPrivateKey()`. The signer address comes from `walletMetadata.accountAddress`.
* **`transaction`** (`VersionedTransaction | Transaction | string`): The transaction to sign

### Optional Parameters

* **`password`** (`string`): Required if the wallet was created with `backUpToDynamic: true`.
* **`externalServerKeyShares`** ([`ServerKeyShare[]`](https://docs.dynamic.xyz/docs/node/reference/types/server-key-share)): Caller-supplied plaintext shares.
* **`sponsor`** (`boolean`): If `true`, requests gas sponsorship from Dynamic before signing. Requires a `Transaction` or `VersionedTransaction` (not a hex string).

## Returns

* **`Promise<string>`**: A **base58-encoded Ed25519 signature** of exactly 88 characters, which decodes to 64 bytes.

> [!WARNING]
>
> **This is the raw signature only, not the full signed transaction.**
>
> Verified return format:
>
> ```
> typeof: string
> length: 88 characters
> decoded bytes: 64  (Ed25519 signature)
> example: "5pkf7k6nDCtoohetqdWoMCJS9Vd..."
> ```
>
> Calling `VersionedTransaction.deserialize()` on the decoded bytes will throw "Reached end of buffer unexpectedly" — confirming the 64 bytes are a signature, not a serialized transaction.
>
> To broadcast: decode the signature and add it back to the transaction using `decodeBase58` and `addSignatureToTransaction` from `@dynamic-labs-wallet/node-svm`, then call `connection.sendRawTransaction(signedTx.serialize())`. See the example below.
>
> Passing the string directly to `connection.sendRawTransaction()` or the SDK's `sendTransaction()` will fail — `Buffer.from(base58string)` interprets the characters as UTF-8 bytes (not the decoded signature), producing a garbled payload.

## Example

```typescript theme={"system"}
import { authenticatedSvmClient } from './client';
import { decodeBase58, addSignatureToTransaction } from '@dynamic-labs-wallet/node-svm';
import {
  Connection, Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey,
} from '@solana/web3.js';

const svmClient = await authenticatedSvmClient();

const walletMetadata = JSON.parse(await redis.get(`wallet:${accountAddress}`));
const externalServerKeyShares = await vault.read(`wallet:${accountAddress}/shares`);

const connection = new Connection(
  process.env.SOLANA_RPC_URL ?? 'https://api.mainnet-beta.solana.com',
  'confirmed'
);
const { blockhash } = await connection.getLatestBlockhash();

const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey(walletMetadata.accountAddress),
    toPubkey: new PublicKey('11111111111111111111111111111112'),
    lamports: LAMPORTS_PER_SOL * 0.001,
  })
);
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(walletMetadata.accountAddress);

// Returns base58 signature (64 bytes), NOT the full signed transaction
const signatureBase58 = await svmClient.signTransaction({
  walletMetadata,
  externalServerKeyShares,
  transaction,
  password: 'user-password',
});

// Decode and attach the signature before broadcasting
const signatureBytes = decodeBase58(signatureBase58);
const signedTx = addSignatureToTransaction({
  transaction,
  signature: signatureBytes,
  signerPublicKey: new PublicKey(walletMetadata.accountAddress),
});

const txid = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(txid, 'confirmed');
console.log('Transaction confirmed:', txid);
```

### With Gas Sponsorship

Pass `sponsor: true` to have Dynamic sponsor the transaction fees. Gas sponsorship must be enabled in the Dynamic Console.

```typescript theme={"system"}
import { authenticatedSvmClient } from './client';
import { decodeBase58, addSignatureToTransaction } from '@dynamic-labs-wallet/node-svm';
import {
  Connection, Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey,
} from '@solana/web3.js';

const svmClient = await authenticatedSvmClient();

const walletMetadata = JSON.parse(await redis.get(`wallet:${accountAddress}`));
const externalServerKeyShares = await vault.read(`wallet:${accountAddress}/shares`);

const connection = new Connection(
  process.env.SOLANA_RPC_URL ?? 'https://api.mainnet-beta.solana.com',
  'confirmed'
);
const { blockhash } = await connection.getLatestBlockhash();

const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey(walletMetadata.accountAddress),
    toPubkey: new PublicKey('11111111111111111111111111111112'),
    lamports: LAMPORTS_PER_SOL * 0.001,
  })
);
transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(walletMetadata.accountAddress);

// sponsor: true replaces the fee payer with the Dynamic sponsor
const signatureBase58 = await svmClient.signTransaction({
  walletMetadata,
  externalServerKeyShares,
  transaction,
  password: 'user-password',
  sponsor: true,
});

const signatureBytes = decodeBase58(signatureBase58);
const signedTx = addSignatureToTransaction({
  transaction,
  signature: signatureBytes,
  signerPublicKey: new PublicKey(walletMetadata.accountAddress),
});

const txid = await connection.sendRawTransaction(signedTx.serialize());
await connection.confirmTransaction(txid, 'confirmed');
console.log('Sponsored transaction confirmed:', txid);
```

## Related

* [`WalletMetadata`](https://docs.dynamic.xyz/docs/node/reference/types/wallet-metadata) - The metadata object passed to every operation
* [`signMessage()`](https://docs.dynamic.xyz/docs/node/reference/svm/sign-message) - Sign a message instead of a transaction
* [`sponsorTransaction()`](https://docs.dynamic.xyz/docs/node/reference/svm/sponsor-transaction) - Sponsor a transaction separately for manual control
* [`createWalletAccount()`](https://docs.dynamic.xyz/docs/node/reference/svm/create-wallet-account) - Create a new wallet account
* [Sign SVM Transactions guide](https://docs.dynamic.xyz/docs/node/svm/sign-transactions.md) - Full guide including pre-built VersionedTransaction patterns


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.